haproxy/src/sample.c

5521 lines
165 KiB
C
Raw Normal View History

/*
* Sample management functions.
*
* Copyright 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
* Copyright (C) 2012 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
#include <ctype.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <import/mjson.h>
#include <import/sha1.h>
#include <haproxy/acl.h>
#include <haproxy/api.h>
#include <haproxy/arg.h>
#include <haproxy/auth.h>
#include <haproxy/base64.h>
#include <haproxy/buf.h>
#include <haproxy/cfgparse.h>
#include <haproxy/chunk.h>
#include <haproxy/clock.h>
#include <haproxy/errors.h>
#include <haproxy/fix.h>
#include <haproxy/global.h>
#include <haproxy/hash.h>
#include <haproxy/http.h>
#include <haproxy/http_ana-t.h>
#include <haproxy/istbuf.h>
#include <haproxy/mqtt.h>
#include <haproxy/net_helper.h>
#include <haproxy/protobuf.h>
#include <haproxy/proxy.h>
#include <haproxy/regex.h>
#include <haproxy/sample.h>
#include <haproxy/sc_strm.h>
#include <haproxy/sink.h>
#include <haproxy/stick_table.h>
#include <haproxy/time.h>
#include <haproxy/tools.h>
#include <haproxy/uri_auth-t.h>
#include <haproxy/vars.h>
#include <haproxy/xxhash.h>
#include <haproxy/jwt.h>
/* sample type names */
const char *smp_to_type[SMP_TYPES] = {
[SMP_T_ANY] = "any",
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
[SMP_T_SAME] = "same",
[SMP_T_BOOL] = "bool",
[SMP_T_SINT] = "sint",
[SMP_T_ADDR] = "addr",
[SMP_T_IPV4] = "ipv4",
[SMP_T_IPV6] = "ipv6",
[SMP_T_STR] = "str",
[SMP_T_BIN] = "bin",
[SMP_T_METH] = "meth",
};
/* Returns SMP_T_* smp matching with <type> name or SMP_TYPES if
* not found.
*/
int type_to_smp(const char *type)
{
int it = 0;
while (it < SMP_TYPES) {
if (strcmp(type, smp_to_type[it]) == 0)
break; // found
it += 1;
}
return it;
}
/* static sample used in sample_process() when <p> is NULL */
static THREAD_LOCAL struct sample temp_smp;
/* list head of all known sample fetch keywords */
static struct sample_fetch_kw_list sample_fetches = {
.list = LIST_HEAD_INIT(sample_fetches.list)
};
/* list head of all known sample format conversion keywords */
static struct sample_conv_kw_list sample_convs = {
.list = LIST_HEAD_INIT(sample_convs.list)
};
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
const unsigned int fetch_cap[SMP_SRC_ENTRIES] = {
[SMP_SRC_CONST] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL_CFG_PARSER |
SMP_VAL_CLI_PARSER ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_INTRN] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL_CLI_PARSER ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_LISTN] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_FTEND] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_L4CLI] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_L5CLI] = (SMP_VAL___________ | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_TRACK] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_L6REQ] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_HRQHV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_HRQHP] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT |
SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_HRQBO] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_BKEND] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_SERVR] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_L4SRV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_L5SRV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_L6RES] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL___________ | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_HRSHV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL___________ | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_HRSHP] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL_FE_LOG_END | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_HRSBO] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
SMP_VAL___________ | SMP_VAL_BE_CHK_RUL | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_RQFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_RSFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_TXFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
[SMP_SRC_SSFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL_FE_LOG_END | SMP_VAL___________ | SMP_VAL___________ |
SMP_VAL___________ ),
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
};
static const char *fetch_src_names[SMP_SRC_ENTRIES] = {
[SMP_SRC_INTRN] = "internal state",
[SMP_SRC_LISTN] = "listener",
[SMP_SRC_FTEND] = "frontend",
[SMP_SRC_L4CLI] = "client address",
[SMP_SRC_L5CLI] = "client-side connection",
[SMP_SRC_TRACK] = "track counters",
[SMP_SRC_L6REQ] = "request buffer",
[SMP_SRC_HRQHV] = "HTTP request headers",
[SMP_SRC_HRQHP] = "HTTP request",
[SMP_SRC_HRQBO] = "HTTP request body",
[SMP_SRC_BKEND] = "backend",
[SMP_SRC_SERVR] = "server",
[SMP_SRC_L4SRV] = "server address",
[SMP_SRC_L5SRV] = "server-side connection",
[SMP_SRC_L6RES] = "response buffer",
[SMP_SRC_HRSHV] = "HTTP response headers",
[SMP_SRC_HRSHP] = "HTTP response",
[SMP_SRC_HRSBO] = "HTTP response body",
[SMP_SRC_RQFIN] = "request buffer statistics",
[SMP_SRC_RSFIN] = "response buffer statistics",
[SMP_SRC_TXFIN] = "transaction statistics",
[SMP_SRC_SSFIN] = "session statistics",
};
static const char *fetch_ckp_names[SMP_CKP_ENTRIES] = {
[SMP_CKP_FE_CON_ACC] = "frontend tcp-request connection rule",
[SMP_CKP_FE_SES_ACC] = "frontend tcp-request session rule",
[SMP_CKP_FE_REQ_CNT] = "frontend tcp-request content rule",
[SMP_CKP_FE_HRQ_HDR] = "frontend http-request header rule",
[SMP_CKP_FE_HRQ_BDY] = "frontend http-request body rule",
[SMP_CKP_FE_SET_BCK] = "frontend use-backend rule",
[SMP_CKP_BE_REQ_CNT] = "backend tcp-request content rule",
[SMP_CKP_BE_HRQ_HDR] = "backend http-request header rule",
[SMP_CKP_BE_HRQ_BDY] = "backend http-request body rule",
[SMP_CKP_BE_SET_SRV] = "backend use-server, balance or stick-match rule",
[SMP_CKP_BE_SRV_CON] = "server source selection",
[SMP_CKP_BE_RES_CNT] = "backend tcp-response content rule",
[SMP_CKP_BE_HRS_HDR] = "backend http-response header rule",
[SMP_CKP_BE_HRS_BDY] = "backend http-response body rule",
[SMP_CKP_BE_STO_RUL] = "backend stick-store rule",
[SMP_CKP_FE_RES_CNT] = "frontend tcp-response content rule",
[SMP_CKP_FE_HRS_HDR] = "frontend http-response header rule",
[SMP_CKP_FE_HRS_BDY] = "frontend http-response body rule",
[SMP_CKP_FE_LOG_END] = "logs",
[SMP_CKP_BE_CHK_RUL] = "backend tcp-check rule",
[SMP_CKP_CFG_PARSER] = "configuration parser",
[SMP_CKP_CLI_PARSER] = "CLI parser",
};
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
/* This function returns the most accurate expected type of the data returned
* by the sample_expr. It assumes that the <expr> and all of its converters are
* properly initialized.
*/
int smp_expr_output_type(struct sample_expr *expr)
{
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
struct sample_conv_expr *cur_smp = NULL;
int cur_type = SMP_T_ANY; /* current type in the chain */
int next_type = SMP_T_ANY; /* next type in the chain */
if (!LIST_ISEMPTY(&expr->conv_exprs)) {
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
/* Ignore converters that output SMP_T_SAME if switching to them is
* conversion-free. (such converter's output match with input, thus only
* their input is considered)
*
* We start looking at the end of conv list and then loop back until the
* sample fetch for better performance (it is more likely to find the last
* effective output type near the end of the chain)
*/
do {
struct list *cur_head = (cur_smp) ? &cur_smp->list : &expr->conv_exprs;
cur_smp = LIST_PREV(cur_head, struct sample_conv_expr *, list);
if (cur_smp->conv->out_type != SMP_T_SAME) {
/* current converter has effective out_type */
cur_type = cur_smp->conv->out_type;
goto out;
}
else if (sample_casts[cur_type][next_type] != c_none)
return next_type; /* switching to next type is not conversion-free */
next_type = cur_smp->conv->in_type;
} while (cur_smp != LIST_NEXT(&expr->conv_exprs, struct sample_conv_expr *, list));
}
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
/* conv list empty or doesn't have effective out_type,
* falling back to sample fetch out_type
*/
cur_type = expr->fetch->out_type;
out:
if (sample_casts[cur_type][next_type] != c_none)
return next_type; /* switching to next type is not conversion-free */
return cur_type;
}
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
/* fill the trash with a comma-delimited list of source names for the <use> bit
* field which must be composed of a non-null set of SMP_USE_* flags. The return
* value is the pointer to the string in the trash buffer.
*/
const char *sample_src_names(unsigned int use)
{
int bit;
trash.data = 0;
trash.area[0] = '\0';
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
for (bit = 0; bit < SMP_SRC_ENTRIES; bit++) {
if (!(use & ~((1 << bit) - 1)))
break; /* no more bits */
if (!(use & (1 << bit)))
continue; /* bit not set */
trash.data += snprintf(trash.area + trash.data,
trash.size - trash.data, "%s%s",
(use & ((1 << bit) - 1)) ? "," : "",
fetch_src_names[bit]);
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
}
return trash.area;
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
}
/* return a pointer to the correct sample checkpoint name, or "unknown" when
* the flags are invalid. Only the lowest bit is used, higher bits are ignored
* if set.
*/
const char *sample_ckp_names(unsigned int use)
{
int bit;
for (bit = 0; bit < SMP_CKP_ENTRIES; bit++)
if (use & (1 << bit))
return fetch_ckp_names[bit];
return "unknown sample check place, please report this bug";
}
/*
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
* Registers the sample fetch keyword list <kwl> as a list of valid keywords
* for next parsing sessions. The fetch keywords capabilities are also computed
* from their ->use field.
*/
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
void sample_register_fetches(struct sample_fetch_kw_list *kwl)
{
MEDIUM: samples: use new flags to describe compatibility between fetches and their usages Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe whether they were compatible with requests rules or with response rules. This was never reliable because we need a finer granularity (eg: an HTTP request method needs to parse an HTTP request, and is available past this point). Some fetches are also dependant on the context (eg: "hdr" uses request or response depending where it's involved, causing some abiguity). In order to solve this, we need to precisely indicate in fetches what they use, and their users will have to compare with what they have. So now we have a bunch of bits indicating where the sample is fetched in the processing chain, with a few variants indicating for some of them if it is permanent or volatile (eg: an HTTP status is stored into the transaction so it is permanent, despite being caught in the response contents). The fetches also have a second mask indicating their validity domain. This one is computed from a conversion table at registration time, so there is no need for doing it by hand. This validity domain consists in a bitmask with one bit set for each usage point in the processing chain. Some provisions were made for upcoming controls such as connection-based TCP rules which apply on top of the connection layer but before instantiating the session. Then everywhere a fetch is used, the bit for the control point is checked in the fetch's validity domain, and it becomes possible to finely ensure that a fetch will work or not. Note that we need these two separate bitfields because some fetches are usable both in request and response (eg: "hdr", "payload"). So the keyword will have a "use" field made of a combination of several SMP_USE_* values, which will be converted into a wider list of SMP_VAL_* flags. The knowledge of permanent vs dynamic information has disappeared for now, as it was never used. Later we'll probably reintroduce it differently when dealing with variables. Its only use at the moment could have been to avoid caching a dynamic rate measurement, but nothing is cached as of now.
2013-01-07 09:42:20 -05:00
struct sample_fetch *sf;
int bit;
for (sf = kwl->kw; sf->kw != NULL; sf++) {
for (bit = 0; bit < SMP_SRC_ENTRIES; bit++)
if (sf->use & (1 << bit))
sf->val |= fetch_cap[bit];
}
LIST_APPEND(&sample_fetches.list, &kwl->list);
}
/*
* Registers the sample format coverstion keyword list <pckl> as a list of valid keywords for next
* parsing sessions.
*/
void sample_register_convs(struct sample_conv_kw_list *pckl)
{
LIST_APPEND(&sample_convs.list, &pckl->list);
}
/*
* Returns the pointer on sample fetch keyword structure identified by
* string of <len> in buffer <kw>.
*
*/
struct sample_fetch *find_sample_fetch(const char *kw, int len)
{
int index;
struct sample_fetch_kw_list *kwl;
list_for_each_entry(kwl, &sample_fetches.list, list) {
for (index = 0; kwl->kw[index].kw != NULL; index++) {
if (strncmp(kwl->kw[index].kw, kw, len) == 0 &&
kwl->kw[index].kw[len] == '\0')
return &kwl->kw[index];
}
}
return NULL;
}
/* dump list of registered sample fetch keywords on stdout */
void smp_dump_fetch_kw(void)
{
struct sample_fetch_kw_list *kwl;
struct sample_fetch *kwp, *kw;
uint64_t mask;
int index;
int arg;
int bit;
for (bit = 0; bit <= SMP_CKP_ENTRIES + 1; bit++) {
putchar('#');
for (index = 0; bit + index <= SMP_CKP_ENTRIES; index++)
putchar(' ');
for (index = 0; index < bit && index < SMP_CKP_ENTRIES; index++)
printf((bit <= SMP_CKP_ENTRIES) ? "/ " : " |");
for (index = bit; bit < SMP_CKP_ENTRIES && index < SMP_CKP_ENTRIES + 2; index++)
if (index == bit)
putchar('_');
else if (index == bit + 1)
putchar('.');
else
putchar('-');
printf(" %s\n", (bit < SMP_CKP_ENTRIES) ? fetch_ckp_names[bit] : "");
}
for (kw = kwp = NULL;; kwp = kw) {
list_for_each_entry(kwl, &sample_fetches.list, list) {
for (index = 0; kwl->kw[index].kw != NULL; index++) {
if (strordered(kwp ? kwp->kw : NULL,
kwl->kw[index].kw,
kw != kwp ? kw->kw : NULL))
kw = &kwl->kw[index];
}
}
if (kw == kwp)
break;
printf("[ ");
for (bit = 0; bit < SMP_CKP_ENTRIES; bit++)
printf("%s", (kw->val & (1 << bit)) ? "Y " : ". ");
printf("] %s", kw->kw);
if (kw->arg_mask) {
mask = kw->arg_mask >> ARGM_BITS;
printf("(");
for (arg = 0;
arg < ARGM_NBARGS && ((mask >> (arg * ARGT_BITS)) & ARGT_MASK);
arg++) {
if (arg == (kw->arg_mask & ARGM_MASK)) {
/* now dumping extra args */
printf("[");
}
if (arg)
printf(",");
printf("%s", arg_type_names[(mask >> (arg * ARGT_BITS)) & ARGT_MASK]);
}
if (arg > (kw->arg_mask & ARGM_MASK)) {
/* extra args were dumped */
printf("]");
}
printf(")");
}
printf(": %s", smp_to_type[kw->out_type]);
printf("\n");
}
}
/* dump list of registered sample converter keywords on stdout */
void smp_dump_conv_kw(void)
{
struct sample_conv_kw_list *kwl;
struct sample_conv *kwp, *kw;
uint64_t mask;
int index;
int arg;
for (kw = kwp = NULL;; kwp = kw) {
list_for_each_entry(kwl, &sample_convs.list, list) {
for (index = 0; kwl->kw[index].kw != NULL; index++) {
if (strordered(kwp ? kwp->kw : NULL,
kwl->kw[index].kw,
kw != kwp ? kw->kw : NULL))
kw = &kwl->kw[index];
}
}
if (kw == kwp)
break;
printf("%s", kw->kw);
if (kw->arg_mask) {
mask = kw->arg_mask >> ARGM_BITS;
printf("(");
for (arg = 0;
arg < ARGM_NBARGS && ((mask >> (arg * ARGT_BITS)) & ARGT_MASK);
arg++) {
if (arg == (kw->arg_mask & ARGM_MASK)) {
/* now dumping extra args */
printf("[");
}
if (arg)
printf(",");
printf("%s", arg_type_names[(mask >> (arg * ARGT_BITS)) & ARGT_MASK]);
}
if (arg > (kw->arg_mask & ARGM_MASK)) {
/* extra args were dumped */
printf("]");
}
printf(")");
}
printf(": %s => %s", smp_to_type[kw->out_type], smp_to_type[kw->in_type]);
printf("\n");
}
}
/* This function browses the list of available sample fetches. <current> is
* the last used sample fetch. If it is the first call, it must set to NULL.
* <idx> is the index of the next sample fetch entry. It is used as private
* value. It is useless to initiate it.
*
* It returns always the new fetch_sample entry, and NULL when the end of
* the list is reached.
*/
struct sample_fetch *sample_fetch_getnext(struct sample_fetch *current, int *idx)
{
struct sample_fetch_kw_list *kwl;
struct sample_fetch *base;
if (!current) {
/* Get first kwl entry. */
kwl = LIST_NEXT(&sample_fetches.list, struct sample_fetch_kw_list *, list);
(*idx) = 0;
} else {
/* Get kwl corresponding to the current entry. */
base = current + 1 - (*idx);
kwl = container_of(base, struct sample_fetch_kw_list, kw);
}
while (1) {
/* Check if kwl is the last entry. */
if (&kwl->list == &sample_fetches.list)
return NULL;
/* idx contain the next keyword. If it is available, return it. */
if (kwl->kw[*idx].kw) {
(*idx)++;
return &kwl->kw[(*idx)-1];
}
/* get next entry in the main list, and return NULL if the end is reached. */
kwl = LIST_NEXT(&kwl->list, struct sample_fetch_kw_list *, list);
/* Set index to 0, ans do one other loop. */
(*idx) = 0;
}
}
/* This function browses the list of available converters. <current> is
* the last used converter. If it is the first call, it must set to NULL.
* <idx> is the index of the next converter entry. It is used as private
* value. It is useless to initiate it.
*
* It returns always the next sample_conv entry, and NULL when the end of
* the list is reached.
*/
struct sample_conv *sample_conv_getnext(struct sample_conv *current, int *idx)
{
struct sample_conv_kw_list *kwl;
struct sample_conv *base;
if (!current) {
/* Get first kwl entry. */
kwl = LIST_NEXT(&sample_convs.list, struct sample_conv_kw_list *, list);
(*idx) = 0;
} else {
/* Get kwl corresponding to the current entry. */
base = current + 1 - (*idx);
kwl = container_of(base, struct sample_conv_kw_list, kw);
}
while (1) {
/* Check if kwl is the last entry. */
if (&kwl->list == &sample_convs.list)
return NULL;
/* idx contain the next keyword. If it is available, return it. */
if (kwl->kw[*idx].kw) {
(*idx)++;
return &kwl->kw[(*idx)-1];
}
/* get next entry in the main list, and return NULL if the end is reached. */
kwl = LIST_NEXT(&kwl->list, struct sample_conv_kw_list *, list);
/* Set index to 0, ans do one other loop. */
(*idx) = 0;
}
}
/*
* Returns the pointer on sample format conversion keyword structure identified by
* string of <len> in buffer <kw>.
*
*/
struct sample_conv *find_sample_conv(const char *kw, int len)
{
int index;
struct sample_conv_kw_list *kwl;
list_for_each_entry(kwl, &sample_convs.list, list) {
for (index = 0; kwl->kw[index].kw != NULL; index++) {
if (strncmp(kwl->kw[index].kw, kw, len) == 0 &&
kwl->kw[index].kw[len] == '\0')
return &kwl->kw[index];
}
}
return NULL;
}
/******************************************************************/
/* Sample casts functions */
/******************************************************************/
static int c_ip2int(struct sample *smp)
{
smp->data.u.sint = ntohl(smp->data.u.ipv4.s_addr);
smp->data.type = SMP_T_SINT;
return 1;
}
static int c_ip2str(struct sample *smp)
{
struct buffer *trash = get_trash_chunk();
if (!inet_ntop(AF_INET, (void *)&smp->data.u.ipv4, trash->area, trash->size))
return 0;
trash->data = strlen(trash->area);
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int c_ip2ipv6(struct sample *smp)
{
v4tov6(&smp->data.u.ipv6, &smp->data.u.ipv4);
smp->data.type = SMP_T_IPV6;
return 1;
}
static int c_ipv62ip(struct sample *smp)
{
if (!v6tov4(&smp->data.u.ipv4, &smp->data.u.ipv6))
return 0;
smp->data.type = SMP_T_IPV4;
return 1;
}
static int c_ipv62str(struct sample *smp)
{
struct buffer *trash = get_trash_chunk();
if (!inet_ntop(AF_INET6, (void *)&smp->data.u.ipv6, trash->area, trash->size))
return 0;
trash->data = strlen(trash->area);
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
/*
static int c_ipv62ip(struct sample *smp)
{
return v6tov4(&smp->data.u.ipv4, &smp->data.u.ipv6);
}
*/
static int c_int2ip(struct sample *smp)
{
smp->data.u.ipv4.s_addr = htonl((unsigned int)smp->data.u.sint);
smp->data.type = SMP_T_IPV4;
return 1;
}
static int c_int2ipv6(struct sample *smp)
{
smp->data.u.ipv4.s_addr = htonl((unsigned int)smp->data.u.sint);
v4tov6(&smp->data.u.ipv6, &smp->data.u.ipv4);
smp->data.type = SMP_T_IPV6;
return 1;
}
static int c_str2addr(struct sample *smp)
{
if (!buf2ip(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv4)) {
if (!buf2ip6(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv6))
return 0;
smp->data.type = SMP_T_IPV6;
smp->flags &= ~SMP_F_CONST;
return 1;
}
smp->data.type = SMP_T_IPV4;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int c_str2ip(struct sample *smp)
{
if (!buf2ip(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv4))
return 0;
smp->data.type = SMP_T_IPV4;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int c_str2ipv6(struct sample *smp)
{
if (!buf2ip6(smp->data.u.str.area, smp->data.u.str.data, &smp->data.u.ipv6))
return 0;
smp->data.type = SMP_T_IPV6;
smp->flags &= ~SMP_F_CONST;
return 1;
}
/*
* The NULL char always enforces the end of string if it is met.
* Data is never changed, so we can ignore the CONST case
*/
static int c_bin2str(struct sample *smp)
{
int i;
for (i = 0; i < smp->data.u.str.data; i++) {
if (!smp->data.u.str.area[i]) {
smp->data.u.str.data = i;
break;
}
}
smp->data.type = SMP_T_STR;
return 1;
}
static int c_int2str(struct sample *smp)
{
struct buffer *trash = get_trash_chunk();
char *pos;
pos = lltoa_r(smp->data.u.sint, trash->area, trash->size);
if (!pos)
return 0;
trash->size = trash->size - (pos - trash->area);
trash->area = pos;
trash->data = strlen(pos);
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
/* This function unconditionally duplicates data and removes the "const" flag.
BUG/MEDIUM: samples: make smp_dup() always duplicate the sample Vedran Furac reported a strange problem where the "base" sample fetch would not always work for tracking purposes. In fact, it happens that commit bc8c404 ("MAJOR: stick-tables: use sample types in place of dedicated types") merged in 1.6 exposed a fundamental bug related to the way samples use chunks as strings. The problem is that chunks convey a base pointer, a length and an optional size, which may be zero when unknown or when the chunk is allocated from a read-only location. The sole purpose of this size is to know whether or not the chunk may be appended new data. This size cause some semantics issue in the sample, which has its own SMP_F_CONST flag to indicate read-only contents. The problem was emphasized by the commit above because it made use of new calls to smp_dup() to convert a sample to a table key. And since smp_dup() would only check the SMP_F_CONST flag, it would happily return read-write samples indicating size=0. So some tests were added upon smp_dup() return to ensure that the actual length is smaller than size, but this in fact made things even worse. For example, the "sni" server directive does some bad stuff on many occasions because it limits len to size-1 and effectively sets it to -1 and writes the zero byte before the beginning of the string! It is therefore obvious that smp_dup() needs to be modified to take this nature of the chunks into account. It's not enough but is needed. The core of the problem comes from the fact that smp_dup() is called for 5 distinct needs which are not always fulfilled : 1) duplicate a sample to keep a copy of it during some operations 2) ensure that the sample is rewritable for a converter like upper() 3) ensure that the sample is terminated with a \0 4) set a correct size on the sample 5) grow the sample in case it was extracted from a partial chunk Case 1 is not used for now, so we can ignore it. Case 2 indicates the wish to modify the sample, so its R/O status must be removed if any, but there's no implied requirement that the chunk becomes larger. Case 3 is used when the sample has to be made compatible with libc's str* functions. There's no need to make it R/W nor to duplicate it if it is already correct. Case 4 can happen when the sample's size is required (eg: before performing some changes that must fit in the buffer). Case 5 is more or less similar but will happen when the sample by be grown but we want to ensure we're not bound by the current small size. So the proposal is to have different functions for various operations. One will ensure a sample is safe for use with str* functions. Another one will ensure it may be rewritten in place. And smp_dup() will have to perform an inconditional duplication to guarantee at least #5 above, and implicitly all other ones. This patch only modifies smp_dup() to make the duplication inconditional. It is enough to fix both the "base" sample fetch and the "sni" server directive, and all use cases in general though not always optimally. More patches will follow to address them more optimally and even better than the current situation (eg: avoid a dup just to add a \0 when possible). The bug comes from an ambiguous design, so its roots are old. 1.6 is affected and a backport is needed. In 1.5, the function already existed but was only used by two converters modifying the data in place, so the bug has no effect there.
2016-08-08 13:21:09 -04:00
* For strings and binary blocks, it also provides a known allocated size with
* a length that is capped to the size, and ensures a trailing zero is always
* appended for strings. This is necessary for some operations which may
* require to extend the length. It returns 0 if it fails, 1 on success.
*/
int smp_dup(struct sample *smp)
{
struct buffer *trash;
switch (smp->data.type) {
case SMP_T_BOOL:
case SMP_T_SINT:
case SMP_T_ADDR:
case SMP_T_IPV4:
case SMP_T_IPV6:
/* These type are not const. */
break;
BUG/MEDIUM: samples: make smp_dup() always duplicate the sample Vedran Furac reported a strange problem where the "base" sample fetch would not always work for tracking purposes. In fact, it happens that commit bc8c404 ("MAJOR: stick-tables: use sample types in place of dedicated types") merged in 1.6 exposed a fundamental bug related to the way samples use chunks as strings. The problem is that chunks convey a base pointer, a length and an optional size, which may be zero when unknown or when the chunk is allocated from a read-only location. The sole purpose of this size is to know whether or not the chunk may be appended new data. This size cause some semantics issue in the sample, which has its own SMP_F_CONST flag to indicate read-only contents. The problem was emphasized by the commit above because it made use of new calls to smp_dup() to convert a sample to a table key. And since smp_dup() would only check the SMP_F_CONST flag, it would happily return read-write samples indicating size=0. So some tests were added upon smp_dup() return to ensure that the actual length is smaller than size, but this in fact made things even worse. For example, the "sni" server directive does some bad stuff on many occasions because it limits len to size-1 and effectively sets it to -1 and writes the zero byte before the beginning of the string! It is therefore obvious that smp_dup() needs to be modified to take this nature of the chunks into account. It's not enough but is needed. The core of the problem comes from the fact that smp_dup() is called for 5 distinct needs which are not always fulfilled : 1) duplicate a sample to keep a copy of it during some operations 2) ensure that the sample is rewritable for a converter like upper() 3) ensure that the sample is terminated with a \0 4) set a correct size on the sample 5) grow the sample in case it was extracted from a partial chunk Case 1 is not used for now, so we can ignore it. Case 2 indicates the wish to modify the sample, so its R/O status must be removed if any, but there's no implied requirement that the chunk becomes larger. Case 3 is used when the sample has to be made compatible with libc's str* functions. There's no need to make it R/W nor to duplicate it if it is already correct. Case 4 can happen when the sample's size is required (eg: before performing some changes that must fit in the buffer). Case 5 is more or less similar but will happen when the sample by be grown but we want to ensure we're not bound by the current small size. So the proposal is to have different functions for various operations. One will ensure a sample is safe for use with str* functions. Another one will ensure it may be rewritten in place. And smp_dup() will have to perform an inconditional duplication to guarantee at least #5 above, and implicitly all other ones. This patch only modifies smp_dup() to make the duplication inconditional. It is enough to fix both the "base" sample fetch and the "sni" server directive, and all use cases in general though not always optimally. More patches will follow to address them more optimally and even better than the current situation (eg: avoid a dup just to add a \0 when possible). The bug comes from an ambiguous design, so its roots are old. 1.6 is affected and a backport is needed. In 1.5, the function already existed but was only used by two converters modifying the data in place, so the bug has no effect there.
2016-08-08 13:21:09 -04:00
case SMP_T_METH:
if (smp->data.u.meth.meth != HTTP_METH_OTHER)
break;
__fallthrough;
case SMP_T_STR:
BUG/MEDIUM: samples: make smp_dup() always duplicate the sample Vedran Furac reported a strange problem where the "base" sample fetch would not always work for tracking purposes. In fact, it happens that commit bc8c404 ("MAJOR: stick-tables: use sample types in place of dedicated types") merged in 1.6 exposed a fundamental bug related to the way samples use chunks as strings. The problem is that chunks convey a base pointer, a length and an optional size, which may be zero when unknown or when the chunk is allocated from a read-only location. The sole purpose of this size is to know whether or not the chunk may be appended new data. This size cause some semantics issue in the sample, which has its own SMP_F_CONST flag to indicate read-only contents. The problem was emphasized by the commit above because it made use of new calls to smp_dup() to convert a sample to a table key. And since smp_dup() would only check the SMP_F_CONST flag, it would happily return read-write samples indicating size=0. So some tests were added upon smp_dup() return to ensure that the actual length is smaller than size, but this in fact made things even worse. For example, the "sni" server directive does some bad stuff on many occasions because it limits len to size-1 and effectively sets it to -1 and writes the zero byte before the beginning of the string! It is therefore obvious that smp_dup() needs to be modified to take this nature of the chunks into account. It's not enough but is needed. The core of the problem comes from the fact that smp_dup() is called for 5 distinct needs which are not always fulfilled : 1) duplicate a sample to keep a copy of it during some operations 2) ensure that the sample is rewritable for a converter like upper() 3) ensure that the sample is terminated with a \0 4) set a correct size on the sample 5) grow the sample in case it was extracted from a partial chunk Case 1 is not used for now, so we can ignore it. Case 2 indicates the wish to modify the sample, so its R/O status must be removed if any, but there's no implied requirement that the chunk becomes larger. Case 3 is used when the sample has to be made compatible with libc's str* functions. There's no need to make it R/W nor to duplicate it if it is already correct. Case 4 can happen when the sample's size is required (eg: before performing some changes that must fit in the buffer). Case 5 is more or less similar but will happen when the sample by be grown but we want to ensure we're not bound by the current small size. So the proposal is to have different functions for various operations. One will ensure a sample is safe for use with str* functions. Another one will ensure it may be rewritten in place. And smp_dup() will have to perform an inconditional duplication to guarantee at least #5 above, and implicitly all other ones. This patch only modifies smp_dup() to make the duplication inconditional. It is enough to fix both the "base" sample fetch and the "sni" server directive, and all use cases in general though not always optimally. More patches will follow to address them more optimally and even better than the current situation (eg: avoid a dup just to add a \0 when possible). The bug comes from an ambiguous design, so its roots are old. 1.6 is affected and a backport is needed. In 1.5, the function already existed but was only used by two converters modifying the data in place, so the bug has no effect there.
2016-08-08 13:21:09 -04:00
trash = get_trash_chunk();
trash->data = smp->data.type == SMP_T_STR ?
smp->data.u.str.data : smp->data.u.meth.str.data;
if (trash->data > trash->size - 1)
trash->data = trash->size - 1;
BUG/MEDIUM: samples: make smp_dup() always duplicate the sample Vedran Furac reported a strange problem where the "base" sample fetch would not always work for tracking purposes. In fact, it happens that commit bc8c404 ("MAJOR: stick-tables: use sample types in place of dedicated types") merged in 1.6 exposed a fundamental bug related to the way samples use chunks as strings. The problem is that chunks convey a base pointer, a length and an optional size, which may be zero when unknown or when the chunk is allocated from a read-only location. The sole purpose of this size is to know whether or not the chunk may be appended new data. This size cause some semantics issue in the sample, which has its own SMP_F_CONST flag to indicate read-only contents. The problem was emphasized by the commit above because it made use of new calls to smp_dup() to convert a sample to a table key. And since smp_dup() would only check the SMP_F_CONST flag, it would happily return read-write samples indicating size=0. So some tests were added upon smp_dup() return to ensure that the actual length is smaller than size, but this in fact made things even worse. For example, the "sni" server directive does some bad stuff on many occasions because it limits len to size-1 and effectively sets it to -1 and writes the zero byte before the beginning of the string! It is therefore obvious that smp_dup() needs to be modified to take this nature of the chunks into account. It's not enough but is needed. The core of the problem comes from the fact that smp_dup() is called for 5 distinct needs which are not always fulfilled : 1) duplicate a sample to keep a copy of it during some operations 2) ensure that the sample is rewritable for a converter like upper() 3) ensure that the sample is terminated with a \0 4) set a correct size on the sample 5) grow the sample in case it was extracted from a partial chunk Case 1 is not used for now, so we can ignore it. Case 2 indicates the wish to modify the sample, so its R/O status must be removed if any, but there's no implied requirement that the chunk becomes larger. Case 3 is used when the sample has to be made compatible with libc's str* functions. There's no need to make it R/W nor to duplicate it if it is already correct. Case 4 can happen when the sample's size is required (eg: before performing some changes that must fit in the buffer). Case 5 is more or less similar but will happen when the sample by be grown but we want to ensure we're not bound by the current small size. So the proposal is to have different functions for various operations. One will ensure a sample is safe for use with str* functions. Another one will ensure it may be rewritten in place. And smp_dup() will have to perform an inconditional duplication to guarantee at least #5 above, and implicitly all other ones. This patch only modifies smp_dup() to make the duplication inconditional. It is enough to fix both the "base" sample fetch and the "sni" server directive, and all use cases in general though not always optimally. More patches will follow to address them more optimally and even better than the current situation (eg: avoid a dup just to add a \0 when possible). The bug comes from an ambiguous design, so its roots are old. 1.6 is affected and a backport is needed. In 1.5, the function already existed but was only used by two converters modifying the data in place, so the bug has no effect there.
2016-08-08 13:21:09 -04:00
memcpy(trash->area, smp->data.type == SMP_T_STR ?
smp->data.u.str.area : smp->data.u.meth.str.area,
trash->data);
trash->area[trash->data] = 0;
BUG/MEDIUM: samples: make smp_dup() always duplicate the sample Vedran Furac reported a strange problem where the "base" sample fetch would not always work for tracking purposes. In fact, it happens that commit bc8c404 ("MAJOR: stick-tables: use sample types in place of dedicated types") merged in 1.6 exposed a fundamental bug related to the way samples use chunks as strings. The problem is that chunks convey a base pointer, a length and an optional size, which may be zero when unknown or when the chunk is allocated from a read-only location. The sole purpose of this size is to know whether or not the chunk may be appended new data. This size cause some semantics issue in the sample, which has its own SMP_F_CONST flag to indicate read-only contents. The problem was emphasized by the commit above because it made use of new calls to smp_dup() to convert a sample to a table key. And since smp_dup() would only check the SMP_F_CONST flag, it would happily return read-write samples indicating size=0. So some tests were added upon smp_dup() return to ensure that the actual length is smaller than size, but this in fact made things even worse. For example, the "sni" server directive does some bad stuff on many occasions because it limits len to size-1 and effectively sets it to -1 and writes the zero byte before the beginning of the string! It is therefore obvious that smp_dup() needs to be modified to take this nature of the chunks into account. It's not enough but is needed. The core of the problem comes from the fact that smp_dup() is called for 5 distinct needs which are not always fulfilled : 1) duplicate a sample to keep a copy of it during some operations 2) ensure that the sample is rewritable for a converter like upper() 3) ensure that the sample is terminated with a \0 4) set a correct size on the sample 5) grow the sample in case it was extracted from a partial chunk Case 1 is not used for now, so we can ignore it. Case 2 indicates the wish to modify the sample, so its R/O status must be removed if any, but there's no implied requirement that the chunk becomes larger. Case 3 is used when the sample has to be made compatible with libc's str* functions. There's no need to make it R/W nor to duplicate it if it is already correct. Case 4 can happen when the sample's size is required (eg: before performing some changes that must fit in the buffer). Case 5 is more or less similar but will happen when the sample by be grown but we want to ensure we're not bound by the current small size. So the proposal is to have different functions for various operations. One will ensure a sample is safe for use with str* functions. Another one will ensure it may be rewritten in place. And smp_dup() will have to perform an inconditional duplication to guarantee at least #5 above, and implicitly all other ones. This patch only modifies smp_dup() to make the duplication inconditional. It is enough to fix both the "base" sample fetch and the "sni" server directive, and all use cases in general though not always optimally. More patches will follow to address them more optimally and even better than the current situation (eg: avoid a dup just to add a \0 when possible). The bug comes from an ambiguous design, so its roots are old. 1.6 is affected and a backport is needed. In 1.5, the function already existed but was only used by two converters modifying the data in place, so the bug has no effect there.
2016-08-08 13:21:09 -04:00
smp->data.u.str = *trash;
break;
case SMP_T_BIN:
trash = get_trash_chunk();
trash->data = smp->data.u.str.data;
if (trash->data > trash->size)
trash->data = trash->size;
BUG/MEDIUM: samples: make smp_dup() always duplicate the sample Vedran Furac reported a strange problem where the "base" sample fetch would not always work for tracking purposes. In fact, it happens that commit bc8c404 ("MAJOR: stick-tables: use sample types in place of dedicated types") merged in 1.6 exposed a fundamental bug related to the way samples use chunks as strings. The problem is that chunks convey a base pointer, a length and an optional size, which may be zero when unknown or when the chunk is allocated from a read-only location. The sole purpose of this size is to know whether or not the chunk may be appended new data. This size cause some semantics issue in the sample, which has its own SMP_F_CONST flag to indicate read-only contents. The problem was emphasized by the commit above because it made use of new calls to smp_dup() to convert a sample to a table key. And since smp_dup() would only check the SMP_F_CONST flag, it would happily return read-write samples indicating size=0. So some tests were added upon smp_dup() return to ensure that the actual length is smaller than size, but this in fact made things even worse. For example, the "sni" server directive does some bad stuff on many occasions because it limits len to size-1 and effectively sets it to -1 and writes the zero byte before the beginning of the string! It is therefore obvious that smp_dup() needs to be modified to take this nature of the chunks into account. It's not enough but is needed. The core of the problem comes from the fact that smp_dup() is called for 5 distinct needs which are not always fulfilled : 1) duplicate a sample to keep a copy of it during some operations 2) ensure that the sample is rewritable for a converter like upper() 3) ensure that the sample is terminated with a \0 4) set a correct size on the sample 5) grow the sample in case it was extracted from a partial chunk Case 1 is not used for now, so we can ignore it. Case 2 indicates the wish to modify the sample, so its R/O status must be removed if any, but there's no implied requirement that the chunk becomes larger. Case 3 is used when the sample has to be made compatible with libc's str* functions. There's no need to make it R/W nor to duplicate it if it is already correct. Case 4 can happen when the sample's size is required (eg: before performing some changes that must fit in the buffer). Case 5 is more or less similar but will happen when the sample by be grown but we want to ensure we're not bound by the current small size. So the proposal is to have different functions for various operations. One will ensure a sample is safe for use with str* functions. Another one will ensure it may be rewritten in place. And smp_dup() will have to perform an inconditional duplication to guarantee at least #5 above, and implicitly all other ones. This patch only modifies smp_dup() to make the duplication inconditional. It is enough to fix both the "base" sample fetch and the "sni" server directive, and all use cases in general though not always optimally. More patches will follow to address them more optimally and even better than the current situation (eg: avoid a dup just to add a \0 when possible). The bug comes from an ambiguous design, so its roots are old. 1.6 is affected and a backport is needed. In 1.5, the function already existed but was only used by two converters modifying the data in place, so the bug has no effect there.
2016-08-08 13:21:09 -04:00
memcpy(trash->area, smp->data.u.str.area, trash->data);
smp->data.u.str = *trash;
break;
default:
/* Other cases are unexpected. */
return 0;
}
/* remove const flag */
smp->flags &= ~SMP_F_CONST;
return 1;
}
int c_none(struct sample *smp)
{
return 1;
}
/* special converter function used by pseudo types in the compatibility matrix
* to inform that the conversion is theoretically allowed at parsing time.
*
* However, being a pseudo type, it may not be emitted by fetches or converters
* so this function should never be called. If this is the case, then it means
* that a pseudo type has been used as a final output type at runtime, which is
* considered as a bug and should be fixed. To help spot this kind of bug, the
* process will crash in this case.
*/
int c_pseudo(struct sample *smp)
{
ABORT_NOW(); // die loudly
/* never reached */
return 0;
}
static int c_str2int(struct sample *smp)
{
const char *str;
const char *end;
if (smp->data.u.str.data == 0)
return 0;
str = smp->data.u.str.area;
end = smp->data.u.str.area + smp->data.u.str.data;
smp->data.u.sint = read_int64(&str, end);
smp->data.type = SMP_T_SINT;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int c_str2meth(struct sample *smp)
{
enum http_meth_t meth;
int len;
meth = find_http_meth(smp->data.u.str.area, smp->data.u.str.data);
if (meth == HTTP_METH_OTHER) {
len = smp->data.u.str.data;
smp->data.u.meth.str.area = smp->data.u.str.area;
smp->data.u.meth.str.data = len;
}
else
smp->flags &= ~SMP_F_CONST;
smp->data.u.meth.meth = meth;
smp->data.type = SMP_T_METH;
return 1;
}
static int c_meth2str(struct sample *smp)
{
int len;
enum http_meth_t meth;
if (smp->data.u.meth.meth == HTTP_METH_OTHER) {
/* The method is unknown. Copy the original pointer. */
len = smp->data.u.meth.str.data;
smp->data.u.str.area = smp->data.u.meth.str.area;
smp->data.u.str.data = len;
smp->data.type = SMP_T_STR;
}
else if (smp->data.u.meth.meth < HTTP_METH_OTHER) {
/* The method is known, copy the pointer containing the string. */
meth = smp->data.u.meth.meth;
smp->data.u.str.area = http_known_methods[meth].ptr;
smp->data.u.str.data = http_known_methods[meth].len;
smp->flags |= SMP_F_CONST;
smp->data.type = SMP_T_STR;
}
else {
/* Unknown method */
return 0;
}
return 1;
}
static int c_addr2bin(struct sample *smp)
{
struct buffer *chk = get_trash_chunk();
if (smp->data.type == SMP_T_IPV4) {
chk->data = 4;
memcpy(chk->area, &smp->data.u.ipv4, chk->data);
}
else if (smp->data.type == SMP_T_IPV6) {
chk->data = 16;
memcpy(chk->area, &smp->data.u.ipv6, chk->data);
}
else
return 0;
smp->data.u.str = *chk;
smp->data.type = SMP_T_BIN;
return 1;
}
static int c_int2bin(struct sample *smp)
{
struct buffer *chk = get_trash_chunk();
*(unsigned long long int *) chk->area = my_htonll(smp->data.u.sint);
chk->data = 8;
smp->data.u.str = *chk;
smp->data.type = SMP_T_BIN;
return 1;
}
static int c_bool2bin(struct sample *smp)
{
struct buffer *chk = get_trash_chunk();
*(unsigned long long int *)chk->area = my_htonll(!!smp->data.u.sint);
chk->data = 8;
smp->data.u.str = *chk;
smp->data.type = SMP_T_BIN;
return 1;
}
/*****************************************************************/
/* Sample casts matrix: */
/* sample_casts[from type][to type] */
/* NULL pointer used for impossible sample casts */
/*****************************************************************/
sample_cast_fct sample_casts[SMP_TYPES][SMP_TYPES] = {
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
/* to: ANY SAME BOOL SINT ADDR IPV4 IPV6 STR BIN METH */
/* from: ANY */ { c_none, NULL, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo },
/* SAME */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
/* BOOL */ { c_none, NULL, c_none, c_none, NULL, NULL, NULL, c_int2str, c_bool2bin, NULL },
/* SINT */ { c_none, NULL, c_none, c_none, c_int2ip, c_int2ip, c_int2ipv6, c_int2str, c_int2bin, NULL },
/* ADDR */ { c_none, NULL, NULL, NULL, c_pseudo, c_pseudo, c_pseudo, c_pseudo, c_pseudo, NULL },
/* IPV4 */ { c_none, NULL, NULL, c_ip2int, c_none, c_none, c_ip2ipv6, c_ip2str, c_addr2bin, NULL },
/* IPV6 */ { c_none, NULL, NULL, NULL, c_none, c_ipv62ip, c_none, c_ipv62str, c_addr2bin, NULL },
/* STR */ { c_none, NULL, c_str2int, c_str2int, c_str2addr, c_str2ip, c_str2ipv6, c_none, c_none, c_str2meth },
/* BIN */ { c_none, NULL, NULL, NULL, NULL, NULL, NULL, c_bin2str, c_none, c_str2meth },
/* METH */ { c_none, NULL, NULL, NULL, NULL, NULL, NULL, c_meth2str, c_meth2str, c_none }
};
/* Process the converters (if any) for a sample expr after the first fetch
* keyword. We have two supported syntaxes for the converters, which can be
* combined:
* - comma-delimited list of converters just after the keyword and args ;
* - one converter per keyword (if <idx> != NULL)
* FIXME: should we continue to support this old syntax?
* The combination allows to have each keyword being a comma-delimited
* series of converters.
*
* We want to process the former first, then the latter. For this we start
* from the beginning of the supposed place in the exiting conv chain, which
* starts at the last comma (<start> which is then referred to as endt).
*
* If <endptr> is non-nul, it will be set to the first unparsed character
* (which may be the final '\0') on success. If it is nul, the expression
* must be properly terminated by a '\0' otherwise an error is reported.
*
* <expr> should point the the sample expression that is already initialized
* with the sample fetch that precedes the converters chain.
*
* The function returns a positive value for success and 0 for failure, in which
* case <err_msg> will point to an allocated string that brings some info
* about the failure. It is the caller's responsibility to free it.
*/
int sample_parse_expr_cnv(char **str, int *idx, char **endptr, char **err_msg, struct arg_list *al, const char *file, int line,
struct sample_expr *expr, const char *start)
{
struct sample_conv *conv;
const char *endt = start; /* end of term */
const char *begw; /* beginning of word */
const char *endw; /* end of word */
char *ckw = NULL;
unsigned long prev_type = expr->fetch->out_type;
int success = 1;
while (1) {
struct sample_conv_expr *conv_expr;
int err_arg;
int argcnt;
if (*endt && *endt != ',') {
if (endptr) {
/* end found, let's stop here */
break;
}
if (ckw)
memprintf(err_msg, "missing comma after converter '%s'", ckw);
else
memprintf(err_msg, "missing comma after fetch keyword");
goto out_error;
}
/* FIXME: how long should we support such idiocies ? Maybe we
* should already warn ?
*/
while (*endt == ',') /* then trailing commas */
endt++;
begw = endt; /* start of converter */
if (!*begw) {
/* none ? skip to next string if idx is set */
if (!idx)
break; /* end of converters */
(*idx)++;
begw = str[*idx];
if (!begw || !*begw)
break;
}
for (endw = begw; is_idchar(*endw); endw++)
;
ha_free(&ckw);
ckw = my_strndup(begw, endw - begw);
conv = find_sample_conv(begw, endw - begw);
if (!conv) {
/* we found an isolated keyword that we don't know, it's not ours */
if (idx && begw == str[*idx]) {
endt = begw;
break;
}
memprintf(err_msg, "unknown converter '%s'", ckw);
goto out_error;
}
if (conv->in_type >= SMP_TYPES || conv->out_type >= SMP_TYPES) {
memprintf(err_msg, "return type of converter '%s' is unknown", ckw);
goto out_error;
}
/* If impossible type conversion */
if (!sample_casts[prev_type][conv->in_type]) {
memprintf(err_msg, "converter '%s' cannot be applied", ckw);
goto out_error;
}
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
/* Ignore converters that output SMP_T_SAME if switching to them is
* conversion-free. (such converter's output match with input, thus only
* their input is considered)
*/
if (conv->out_type != SMP_T_SAME)
prev_type = conv->out_type;
else if (sample_casts[prev_type][conv->in_type] != c_none)
prev_type = conv->in_type;
conv_expr = calloc(1, sizeof(*conv_expr));
if (!conv_expr)
goto out_error;
LIST_APPEND(&(expr->conv_exprs), &(conv_expr->list));
conv_expr->conv = conv;
if (al) {
al->kw = expr->fetch->kw;
al->conv = conv_expr->conv->kw;
}
argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err_msg, &endt, &err_arg, al);
if (argcnt < 0) {
memprintf(err_msg, "invalid arg %d in converter '%s' : %s", err_arg+1, ckw, *err_msg);
goto out_error;
}
if (argcnt && !conv->arg_mask) {
memprintf(err_msg, "converter '%s' does not support any args", ckw);
goto out_error;
}
if (!conv_expr->arg_p)
conv_expr->arg_p = empty_arg_list;
if (conv->val_args && !conv->val_args(conv_expr->arg_p, conv, file, line, err_msg)) {
memprintf(err_msg, "invalid args in converter '%s' : %s", ckw, *err_msg);
goto out_error;
}
}
if (endptr) {
/* end found, let's stop here */
*endptr = (char *)endt;
}
out:
free(ckw);
return success;
out_error:
success = 0;
goto out;
}
/*
* Parse a sample expression configuration:
* fetch keyword followed by format conversion keywords.
*
* <al> is an arg_list serving as a list head to report missing dependencies.
* It may be NULL if such dependencies are not allowed. Otherwise, the caller
* must have set al->ctx if al is set.
*
* Returns a pointer on allocated sample expression structure or NULL in case
* of error, in which case <err_msg> will point to an allocated string that
* brings some info about the failure. It is the caller's responsibility to
* free it.
*/
struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, int line, char **err_msg, struct arg_list *al, char **endptr)
{
const char *begw; /* beginning of word */
const char *endw; /* end of word */
const char *endt; /* end of term */
struct sample_expr *expr = NULL;
struct sample_fetch *fetch;
char *fkw = NULL;
int err_arg;
begw = str[*idx];
for (endw = begw; is_idchar(*endw); endw++)
;
if (endw == begw) {
memprintf(err_msg, "missing fetch method");
goto out_error;
}
/* keep a copy of the current fetch keyword for error reporting */
fkw = my_strndup(begw, endw - begw);
fetch = find_sample_fetch(begw, endw - begw);
if (!fetch) {
memprintf(err_msg, "unknown fetch method '%s'", fkw);
goto out_error;
}
/* At this point, we have :
* - begw : beginning of the keyword
* - endw : end of the keyword, first character not part of keyword
*/
if (fetch->out_type >= SMP_TYPES) {
memprintf(err_msg, "returns type of fetch method '%s' is unknown", fkw);
goto out_error;
}
expr = calloc(1, sizeof(*expr));
if (!expr)
goto out_error;
LIST_INIT(&(expr->conv_exprs));
expr->fetch = fetch;
expr->arg_p = empty_arg_list;
/* Note that we call the argument parser even with an empty string,
* this allows it to automatically create entries for mandatory
* implicit arguments (eg: local proxy name).
*/
if (al) {
al->kw = expr->fetch->kw;
al->conv = NULL;
}
if (make_arg_list(endw, -1, fetch->arg_mask, &expr->arg_p, err_msg, &endt, &err_arg, al) < 0) {
memprintf(err_msg, "fetch method '%s' : %s", fkw, *err_msg);
goto out_error;
}
/* now endt is our first char not part of the arg list, typically the
* comma after the sample fetch name or after the closing parenthesis,
* or the NUL char.
*/
if (!expr->arg_p) {
expr->arg_p = empty_arg_list;
}
else if (fetch->val_args && !fetch->val_args(expr->arg_p, err_msg)) {
memprintf(err_msg, "invalid args in fetch method '%s' : %s", fkw, *err_msg);
goto out_error;
}
if (!sample_parse_expr_cnv(str, idx, endptr, err_msg, al, file, line, expr, endt))
goto out_error;
out:
free(fkw);
return expr;
out_error:
release_sample_expr(expr);
expr = NULL;
goto out;
}
/*
* Helper function to process the converter list of a given sample expression
* <expr> using the sample <p> (which is assumed to be properly initialized)
* as input.
*
* Returns 1 on success and 0 on failure.
*/
int sample_process_cnv(struct sample_expr *expr, struct sample *p)
{
struct sample_conv_expr *conv_expr;
list_for_each_entry(conv_expr, &expr->conv_exprs, list) {
/* we want to ensure that p->type can be casted into
* conv_expr->conv->in_type. We have 3 possibilities :
* - NULL => not castable.
* - c_none => nothing to do (let's optimize it)
* - other => apply cast and prepare to fail
*/
if (p->data.type != conv_expr->conv->in_type) {
if (!sample_casts[p->data.type][conv_expr->conv->in_type])
return 0;
if (sample_casts[p->data.type][conv_expr->conv->in_type] != c_none &&
!sample_casts[p->data.type][conv_expr->conv->in_type](p))
return 0;
}
/* OK cast succeeded */
if (!conv_expr->conv->process(conv_expr->arg_p, p, conv_expr->conv->private))
return 0;
}
return 1;
}
/*
* Process a fetch + format conversion of defined by the sample expression <expr>
* on request or response considering the <opt> parameter.
* Returns a pointer on a typed sample structure containing the result or NULL if
* sample is not found or when format conversion failed.
* If <p> is not null, function returns results in structure pointed by <p>.
* If <p> is null, functions returns a pointer on a static sample structure.
*
* Note: the fetch functions are required to properly set the return type. The
* conversion functions must do so too. However the cast functions do not need
* to since they're made to cast multiple types according to what is required.
*
* The caller may indicate in <opt> if it considers the result final or not.
* The caller needs to check the SMP_F_MAY_CHANGE flag in p->flags to verify
* if the result is stable or not, according to the following table :
*
* return MAY_CHANGE FINAL Meaning for the sample
* NULL 0 * Not present and will never be (eg: header)
* NULL 1 0 Not present yet, could change (eg: POST param)
* NULL 1 1 Not present yet, will not change anymore
* smp 0 * Present and will not change (eg: header)
* smp 1 0 Present, may change (eg: request length)
* smp 1 1 Present, last known value (eg: request length)
*/
struct sample *sample_process(struct proxy *px, struct session *sess,
struct stream *strm, unsigned int opt,
struct sample_expr *expr, struct sample *p)
{
if (p == NULL) {
p = &temp_smp;
memset(p, 0, sizeof(*p));
}
smp_set_owner(p, px, sess, strm, opt);
if (!expr->fetch->process(expr->arg_p, p, expr->fetch->kw, expr->fetch->private))
return NULL;
if (!sample_process_cnv(expr, p))
return NULL;
return p;
}
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
/*
* Resolve all remaining arguments in proxy <p>. Returns the number of
* errors or 0 if everything is fine. If at least one error is met, it will
* be appended to *err. If *err==NULL it will be allocated first.
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
*/
int smp_resolve_args(struct proxy *p, char **err)
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
{
struct arg_list *cur, *bak;
const char *ctx, *where;
const char *conv_ctx, *conv_pre, *conv_pos;
struct userlist *ul;
struct my_regex *reg;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
struct arg *arg;
int cfgerr = 0;
int rflags;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
list_for_each_entry_safe(cur, bak, &p->conf.args.list, list) {
struct proxy *px;
struct server *srv;
MEDIUM: stick-table: Stop handling stick-tables as proxies. This patch adds the support for the "table" line parsing in "peers" sections to declare stick-table in such sections. This also prevents the user from having to declare dummy backends sections with a unique stick-table inside. Even if still supported, this usage will become deprecated. To do so, the ->table member of proxy struct which is a stktable struct is replaced by a pointer to a stktable struct allocated at parsing time in src/cfgparse-listen.c for the dummy stick-table backends and in src/cfgparse.c for "peers" sections. This has an impact on the code for stick-table sample converters and on the stickiness rules parsers which first store the name of the dummy before resolving the rules. This patch replaces proxy_tbl_by_name() calls by stktable_find_by_name() calls to lookup for stick-tables stored in "stktable_by_name" ebtree at parsing time. There is only one remaining place where proxy_tbl_by_name() is used: src/hlua.c. At several places in the code we relied on the fact that ->size member of stick-table was equal to zero to consider the stick-table was present by not configured, this do not make sense anymore as ->table member of struct proxyis fow now on a pointer. These tests are replaced by a test on ->table value itself. In "peers" section we do not have to temporary store the name of the section the stick-table are attached to because this name is obviously already known just after having entered this "peers" section. About the CLI stick-table I/O handler, the pointer to proxy struct is replaced by a pointer to a stktable struct.
2019-03-14 02:07:41 -04:00
struct stktable *t;
char *pname, *sname, *stktname;
char *err2;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
arg = cur->arg;
/* prepare output messages */
conv_pre = conv_pos = conv_ctx = "";
if (cur->conv) {
conv_ctx = cur->conv;
conv_pre = "conversion keyword '";
conv_pos = "' for ";
}
where = "in";
ctx = "sample fetch keyword";
switch (cur->ctx) {
case ARGC_STK: where = "in stick rule in"; break;
case ARGC_TRK: where = "in tracking rule in"; break;
case ARGC_LOG: where = "in log-format string in"; break;
case ARGC_LOGSD: where = "in log-format-sd string in"; break;
case ARGC_HRQ: where = "in http-request expression in"; break;
case ARGC_HRS: where = "in http-response response in"; break;
case ARGC_UIF: where = "in unique-id-format string in"; break;
case ARGC_RDR: where = "in redirect format string in"; break;
case ARGC_CAP: where = "in capture rule in"; break;
case ARGC_ACL: ctx = "ACL keyword"; break;
case ARGC_SRV: where = "in server directive in"; break;
case ARGC_SPOE: where = "in spoe-message directive in"; break;
case ARGC_UBK: where = "in use_backend expression in"; break;
case ARGC_USRV: where = "in use-server or balance expression in"; break;
case ARGC_HERR: where = "in http-error directive in"; break;
case ARGC_OT: where = "in ot-scope directive in"; break;
case ARGC_OPT: where = "in option directive in"; break;
case ARGC_TCO: where = "in tcp-request connection expression in"; break;
case ARGC_TSE: where = "in tcp-request session expression in"; break;
case ARGC_TRQ: where = "in tcp-request content expression in"; break;
case ARGC_TRS: where = "in tcp-response content expression in"; break;
case ARGC_TCK: where = "in tcp-check expression in"; break;
case ARGC_CFG: where = "in configuration expression in"; break;
case ARGC_CLI: where = "in CLI expression in"; break;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
}
/* set a few default settings */
px = p;
pname = p->id;
switch (arg->type) {
case ARGT_ID:
err2 = NULL;
if (arg->resolve_ptr && !arg->resolve_ptr(arg, &err2)) {
memprintf(err, "%sparsing [%s:%d]: error in identifier '%s' in arg %d of %s%s%s%s '%s' %s proxy '%s' : %s.\n",
*err ? *err : "", cur->file, cur->line,
arg->data.str.area,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id, err2);
ha_free(&err2);
cfgerr++;
continue;
}
break;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
case ARGT_SRV:
if (!arg->data.str.data) {
memprintf(err, "%sparsing [%s:%d]: missing server name in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
continue;
}
/* we support two formats : "bck/srv" and "srv" */
sname = strrchr(arg->data.str.area, '/');
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
if (sname) {
*sname++ = '\0';
pname = arg->data.str.area;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
px = proxy_be_by_name(pname);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
if (!px) {
memprintf(err, "%sparsing [%s:%d]: unable to find proxy '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line, pname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
}
else {
if (px->cap & PR_CAP_DEF) {
memprintf(err, "%sparsing [%s:%d]: backend name must be set in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
cfgerr++;
break;
}
sname = arg->data.str.area;
}
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
srv = server_find_by_name(px, sname);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
if (!srv) {
memprintf(err, "%sparsing [%s:%d]: unable to find server '%s' in proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line, sname, pname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
srv->flags |= SRV_F_NON_PURGEABLE;
chunk_destroy(&arg->data.str);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
arg->unresolved = 0;
arg->data.srv = srv;
break;
case ARGT_FE:
if (arg->data.str.data) {
pname = arg->data.str.area;
px = proxy_fe_by_name(pname);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
}
if (!px) {
memprintf(err, "%sparsing [%s:%d]: unable to find frontend '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line, pname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
if (!(px->cap & PR_CAP_FE)) {
memprintf(err, "%sparsing [%s:%d]: proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s', has not frontend capability.\n",
*err ? *err : "", cur->file, cur->line, pname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
chunk_destroy(&arg->data.str);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
arg->unresolved = 0;
arg->data.prx = px;
break;
case ARGT_BE:
if (arg->data.str.data) {
pname = arg->data.str.area;
px = proxy_be_by_name(pname);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
}
if (!px) {
memprintf(err, "%sparsing [%s:%d]: unable to find backend '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line, pname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
if (!(px->cap & PR_CAP_BE)) {
memprintf(err, "%sparsing [%s:%d]: proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s', has not backend capability.\n",
*err ? *err : "", cur->file, cur->line, pname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
chunk_destroy(&arg->data.str);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
arg->unresolved = 0;
arg->data.prx = px;
break;
case ARGT_TAB:
if (arg->data.str.data)
stktname = arg->data.str.area;
else {
if (px->cap & PR_CAP_DEF) {
memprintf(err, "%sparsing [%s:%d]: table name must be set in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
cfgerr++;
break;
}
stktname = px->id;
}
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
MEDIUM: stick-table: Stop handling stick-tables as proxies. This patch adds the support for the "table" line parsing in "peers" sections to declare stick-table in such sections. This also prevents the user from having to declare dummy backends sections with a unique stick-table inside. Even if still supported, this usage will become deprecated. To do so, the ->table member of proxy struct which is a stktable struct is replaced by a pointer to a stktable struct allocated at parsing time in src/cfgparse-listen.c for the dummy stick-table backends and in src/cfgparse.c for "peers" sections. This has an impact on the code for stick-table sample converters and on the stickiness rules parsers which first store the name of the dummy before resolving the rules. This patch replaces proxy_tbl_by_name() calls by stktable_find_by_name() calls to lookup for stick-tables stored in "stktable_by_name" ebtree at parsing time. There is only one remaining place where proxy_tbl_by_name() is used: src/hlua.c. At several places in the code we relied on the fact that ->size member of stick-table was equal to zero to consider the stick-table was present by not configured, this do not make sense anymore as ->table member of struct proxyis fow now on a pointer. These tests are replaced by a test on ->table value itself. In "peers" section we do not have to temporary store the name of the section the stick-table are attached to because this name is obviously already known just after having entered this "peers" section. About the CLI stick-table I/O handler, the pointer to proxy struct is replaced by a pointer to a stktable struct.
2019-03-14 02:07:41 -04:00
t = stktable_find_by_name(stktname);
if (!t) {
memprintf(err, "%sparsing [%s:%d]: unable to find table '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line, stktname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
MEDIUM: stick-table: Stop handling stick-tables as proxies. This patch adds the support for the "table" line parsing in "peers" sections to declare stick-table in such sections. This also prevents the user from having to declare dummy backends sections with a unique stick-table inside. Even if still supported, this usage will become deprecated. To do so, the ->table member of proxy struct which is a stktable struct is replaced by a pointer to a stktable struct allocated at parsing time in src/cfgparse-listen.c for the dummy stick-table backends and in src/cfgparse.c for "peers" sections. This has an impact on the code for stick-table sample converters and on the stickiness rules parsers which first store the name of the dummy before resolving the rules. This patch replaces proxy_tbl_by_name() calls by stktable_find_by_name() calls to lookup for stick-tables stored in "stktable_by_name" ebtree at parsing time. There is only one remaining place where proxy_tbl_by_name() is used: src/hlua.c. At several places in the code we relied on the fact that ->size member of stick-table was equal to zero to consider the stick-table was present by not configured, this do not make sense anymore as ->table member of struct proxyis fow now on a pointer. These tests are replaced by a test on ->table value itself. In "peers" section we do not have to temporary store the name of the section the stick-table are attached to because this name is obviously already known just after having entered this "peers" section. About the CLI stick-table I/O handler, the pointer to proxy struct is replaced by a pointer to a stktable struct.
2019-03-14 02:07:41 -04:00
if (!t->size) {
memprintf(err, "%sparsing [%s:%d]: no table in proxy '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line, stktname,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
if (!in_proxies_list(t->proxies_list, p)) {
p->next_stkt_ref = t->proxies_list;
t->proxies_list = p;
}
chunk_destroy(&arg->data.str);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
arg->unresolved = 0;
MEDIUM: stick-table: Stop handling stick-tables as proxies. This patch adds the support for the "table" line parsing in "peers" sections to declare stick-table in such sections. This also prevents the user from having to declare dummy backends sections with a unique stick-table inside. Even if still supported, this usage will become deprecated. To do so, the ->table member of proxy struct which is a stktable struct is replaced by a pointer to a stktable struct allocated at parsing time in src/cfgparse-listen.c for the dummy stick-table backends and in src/cfgparse.c for "peers" sections. This has an impact on the code for stick-table sample converters and on the stickiness rules parsers which first store the name of the dummy before resolving the rules. This patch replaces proxy_tbl_by_name() calls by stktable_find_by_name() calls to lookup for stick-tables stored in "stktable_by_name" ebtree at parsing time. There is only one remaining place where proxy_tbl_by_name() is used: src/hlua.c. At several places in the code we relied on the fact that ->size member of stick-table was equal to zero to consider the stick-table was present by not configured, this do not make sense anymore as ->table member of struct proxyis fow now on a pointer. These tests are replaced by a test on ->table value itself. In "peers" section we do not have to temporary store the name of the section the stick-table are attached to because this name is obviously already known just after having entered this "peers" section. About the CLI stick-table I/O handler, the pointer to proxy struct is replaced by a pointer to a stktable struct.
2019-03-14 02:07:41 -04:00
arg->data.t = t;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
break;
case ARGT_USR:
if (!arg->data.str.data) {
memprintf(err, "%sparsing [%s:%d]: missing userlist name in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
if (p->uri_auth && p->uri_auth->userlist &&
CLEANUP: Compare the return value of `XXXcmp()` functions with zero According to coding-style.txt it is recommended to use: `strcmp(a, b) == 0` instead of `!strcmp(a, b)` So let's do this. The change was performed by running the following (very long) coccinelle patch on src/: @@ statement S; expression E; expression F; @@ if ( ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) != 0 ) ( S | { ... } ) @@ statement S; expression E; expression F; @@ if ( - ! ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) == 0 ) ( S | { ... } ) @@ expression E; expression F; expression G; @@ ( G && ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) != 0 ) @@ expression E; expression F; expression G; @@ ( G || ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) != 0 ) @@ expression E; expression F; expression G; @@ ( ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) != 0 && G ) @@ expression E; expression F; expression G; @@ ( ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) != 0 || G ) @@ expression E; expression F; expression G; @@ ( G && - ! ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) == 0 ) @@ expression E; expression F; expression G; @@ ( G || - ! ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) == 0 ) @@ expression E; expression F; expression G; @@ ( - ! ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) == 0 && G ) @@ expression E; expression F; expression G; @@ ( - ! ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) == 0 || G ) @@ expression E; expression F; expression G; @@ ( - ! ( dns_hostname_cmp | eb_memcmp | memcmp | strcasecmp | strcmp | strncasecmp | strncmp ) - (E, F) + (E, F) == 0 )
2021-01-02 16:31:53 -05:00
strcmp(p->uri_auth->userlist->name, arg->data.str.area) == 0)
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
ul = p->uri_auth->userlist;
else
ul = auth_find_userlist(arg->data.str.area);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
if (!ul) {
memprintf(err, "%sparsing [%s:%d]: unable to find userlist '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line,
arg->data.str.area,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
cfgerr++;
break;
}
chunk_destroy(&arg->data.str);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
arg->unresolved = 0;
arg->data.usr = ul;
break;
case ARGT_REG:
if (!arg->data.str.data) {
memprintf(err, "%sparsing [%s:%d]: missing regex in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
*err ? *err : "", cur->file, cur->line,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
cfgerr++;
continue;
}
rflags = 0;
rflags |= (arg->type_flags & ARGF_REG_ICASE) ? REG_ICASE : 0;
err2 = NULL;
if (!(reg = regex_comp(arg->data.str.area, !(rflags & REG_ICASE), 1 /* capture substr */, &err2))) {
memprintf(err, "%sparsing [%s:%d]: error in regex '%s' in arg %d of %s%s%s%s '%s' %s proxy '%s' : %s.\n",
*err ? *err : "", cur->file, cur->line,
arg->data.str.area,
cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id, err2);
ha_free(&err2);
cfgerr++;
continue;
}
chunk_destroy(&arg->data.str);
arg->unresolved = 0;
arg->data.reg = reg;
break;
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
}
LIST_DELETE(&cur->list);
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve While ACL args were resolved after all the config was parsed, it was not the case with sample fetch args because they're almost everywhere now. The issue is that ACLs now solely rely on sample fetches, so their args resolving doesn't work anymore. And many fetches involving a server, a proxy or a userlist don't work at all. The real issue is that at the bottom layers we have no information about proxies, line numbers, even ACLs in order to report understandable errors, and that at the top layers we have no visibility over the locations where fetches are referenced (think log node). After failing multiple unsatisfying solutions attempts, we now have a new concept of args list. The principle is that every proxy has a list head which contains a number of indications such as the config keyword, the context where it's used, the file and line number, etc... and a list of arguments. This list head is of the same type as the elements, so it serves as a template for adding new elements. This way, it is filled from top to bottom by the callers with the information they have (eg: line numbers, ACL name, ...) and the lower layers just have to duplicate it and add an element when they face an argument they cannot resolve yet. Then at the end of the configuration parsing, a loop passes over each proxy's list and resolves all the args in sequence. And this way there is all necessary information to report verbose errors. The first immediate benefit is that for the first time we got very precise location of issues (arg number in a keyword in its context, ...). Second, in order to do this we had to parse log-format and unique-id-format a bit earlier, so that was a great opportunity for doing so when the directives are encountered (unless it's a default section). This way, the recorded line numbers for these args are the ones of the place where the log format is declared, not the end of the file. Userlists report slightly more information now. They're the only remaining ones in the ACL resolving function.
2013-04-02 10:34:32 -04:00
free(cur);
} /* end of args processing */
return cfgerr;
}
/*
* Process a fetch + format conversion as defined by the sample expression
* <expr> on request or response considering the <opt> parameter. The output is
* not explicitly set to <smp_type>, but shall be compatible with it as
* specified by 'sample_casts' table. If a stable sample can be fetched, or an
* unstable one when <opt> contains SMP_OPT_FINAL, the sample is converted and
* returned without the SMP_F_MAY_CHANGE flag. If an unstable sample is found
* and <opt> does not contain SMP_OPT_FINAL, then the sample is returned as-is
* with its SMP_F_MAY_CHANGE flag so that the caller can check it and decide to
* take actions (eg: wait longer). If a sample could not be found or could not
* be converted, NULL is returned. The caller MUST NOT use the sample if the
* SMP_F_MAY_CHANGE flag is present, as it is used only as a hint that there is
* still hope to get it after waiting longer, and is not converted to string.
* The possible output combinations are the following :
*
* return MAY_CHANGE FINAL Meaning for the sample
* NULL * * Not present and will never be (eg: header)
* smp 0 * Final value converted (eg: header)
* smp 1 0 Not present yet, may appear later (eg: header)
* smp 1 1 never happens (either flag is cleared on output)
*/
struct sample *sample_fetch_as_type(struct proxy *px, struct session *sess,
struct stream *strm, unsigned int opt,
struct sample_expr *expr, int smp_type)
{
struct sample *smp = &temp_smp;
memset(smp, 0, sizeof(*smp));
if (!sample_process(px, sess, strm, opt, expr, smp)) {
if ((smp->flags & SMP_F_MAY_CHANGE) && !(opt & SMP_OPT_FINAL))
return smp;
return NULL;
}
if (smp->data.type != smp_type) {
if (!sample_casts[smp->data.type][smp_type])
return NULL;
if (sample_casts[smp->data.type][smp_type] != c_none &&
!sample_casts[smp->data.type][smp_type](smp))
return NULL;
}
smp->flags &= ~SMP_F_MAY_CHANGE;
return smp;
}
static void release_sample_arg(struct arg *p)
{
struct arg *p_back = p;
if (!p)
return;
while (p->type != ARGT_STOP) {
if (p->type == ARGT_STR || p->unresolved) {
chunk_destroy(&p->data.str);
p->unresolved = 0;
}
else if (p->type == ARGT_REG) {
regex_free(p->data.reg);
p->data.reg = NULL;
}
p++;
}
if (p_back != empty_arg_list)
free(p_back);
}
void release_sample_expr(struct sample_expr *expr)
{
struct sample_conv_expr *conv_expr, *conv_exprb;
if (!expr)
return;
list_for_each_entry_safe(conv_expr, conv_exprb, &expr->conv_exprs, list) {
LIST_DELETE(&conv_expr->list);
release_sample_arg(conv_expr->arg_p);
free(conv_expr);
}
release_sample_arg(expr->arg_p);
free(expr);
}
/*****************************************************************/
/* Sample format convert functions */
/* These functions set the data type on return. */
/*****************************************************************/
static int sample_conv_debug(const struct arg *arg_p, struct sample *smp, void *private)
{
int i;
struct sample tmp;
struct buffer *buf;
struct sink *sink;
struct ist line;
char *pfx;
buf = alloc_trash_chunk();
if (!buf)
goto end;
sink = (struct sink *)arg_p[1].data.ptr;
BUG_ON(!sink);
pfx = arg_p[0].data.str.area;
BUG_ON(!pfx);
chunk_printf(buf, "[debug] %s: type=%s ", pfx, smp_to_type[smp->data.type]);
if (!sample_casts[smp->data.type][SMP_T_STR])
goto nocast;
/* Copy sample fetch. This puts the sample as const, the
* cast will copy data if a transformation is required.
*/
memcpy(&tmp, smp, sizeof(struct sample));
tmp.flags = SMP_F_CONST;
if (!sample_casts[smp->data.type][SMP_T_STR](&tmp))
goto nocast;
/* Display the displayable chars*. */
b_putchr(buf, '<');
for (i = 0; i < tmp.data.u.str.data; i++) {
if (isprint((unsigned char)tmp.data.u.str.area[i]))
b_putchr(buf, tmp.data.u.str.area[i]);
else
b_putchr(buf, '.');
}
b_putchr(buf, '>');
done:
line = ist2(buf->area, buf->data);
sink_write(sink, LOG_HEADER_NONE, 0, &line, 1);
end:
free_trash_chunk(buf);
return 1;
nocast:
chunk_appendf(buf, "(undisplayable)");
goto done;
}
// This function checks the "debug" converter's arguments.
static int smp_check_debug(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
const char *name = "buf0";
struct sink *sink = NULL;
if (args[0].type != ARGT_STR) {
/* optional prefix */
args[0].data.str.area = "";
args[0].data.str.data = 0;
}
if (args[1].type == ARGT_STR)
name = args[1].data.str.area;
sink = sink_find_early(name, "debug converter", file, line);
if (!sink) {
memprintf(err, "Memory error while setting up sink '%s'", name);
return 0;
}
chunk_destroy(&args[1].data.str);
args[1].type = ARGT_PTR;
args[1].data.ptr = sink;
return 1;
}
static int sample_conv_base642bin(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
int bin_len;
trash->data = 0;
bin_len = base64dec(smp->data.u.str.area, smp->data.u.str.data,
trash->area, trash->size);
if (bin_len < 0)
return 0;
trash->data = bin_len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_BIN;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int sample_conv_base64url2bin(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
int bin_len;
trash->data = 0;
bin_len = base64urldec(smp->data.u.str.area, smp->data.u.str.data,
trash->area, trash->size);
if (bin_len < 0)
return 0;
trash->data = bin_len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_BIN;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
int b64_len;
trash->data = 0;
b64_len = a2base64(smp->data.u.str.area, smp->data.u.str.data,
trash->area, trash->size);
if (b64_len < 0)
return 0;
trash->data = b64_len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int sample_conv_bin2base64url(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
int b64_len;
trash->data = 0;
b64_len = a2base64url(smp->data.u.str.area, smp->data.u.str.data,
trash->area, trash->size);
if (b64_len < 0)
return 0;
trash->data = b64_len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
/* This function returns a sample struct filled with the conversion of variable
* <var> to sample type <type> (SMP_T_*), via a cast to the target type. If the
* variable cannot be retrieved or casted, 0 is returned, otherwise 1.
*
* Keep in mind that the sample content may be written to a pre-allocated
* trash chunk as returned by get_trash_chunk().
*/
int sample_conv_var2smp(const struct var_desc *var, struct sample *smp, int type)
{
if (!vars_get_by_desc(var, smp, NULL))
return 0;
if (!sample_casts[smp->data.type][type])
return 0;
if (!sample_casts[smp->data.type][type](smp))
return 0;
return 1;
}
static int sample_conv_sha1(const struct arg *arg_p, struct sample *smp, void *private)
{
blk_SHA_CTX ctx;
struct buffer *trash = get_trash_chunk();
memset(&ctx, 0, sizeof(ctx));
blk_SHA1_Init(&ctx);
blk_SHA1_Update(&ctx, smp->data.u.str.area, smp->data.u.str.data);
blk_SHA1_Final((unsigned char *) trash->area, &ctx);
trash->data = 20;
smp->data.u.str = *trash;
smp->data.type = SMP_T_BIN;
smp->flags &= ~SMP_F_CONST;
return 1;
}
BUG/MINOR: sample: secure convs that accept base64 string and var name as args This patch adds a few improvements in order to secure the use of converters that accept base64 string and variable name as arguments. The first change is within related function sample_conv_var2smp_str() which now flags the sample as SMP_F_CONST if the argument is of type ARGT_STR. This makes the sample more safe for later use. A new function sample_check_arg_base64() is added. It checks an argument and fills it with a variable type if the argument string contains a valid variable name. If failed, it tries to perform a base64 decode operation on a non-empty string, and fills the argument with the decoded content which can be used later, without any additional base64dec() function calls during runtime. This means that haproxy configuration check may fail if variable lookup fails and an invalid base64 encoded string is specified as an argument for such converters. Both converters, "aes_gcm_dec" and "hmac", now use alloc_trash_chunk() in order to allocate additional buffers for various conversions, and avoid the use of a pre-allocated trash chunks directly (usually returned by get_trash_chunk()). The function sample_check_arg_base64() is used for both converters in order to check their arguments specified within the haproxy configuration. This patch should be backported as far as 2.0. However, it is important to keep in mind a few things. The "hmac" converter is only available starting with 2.2. In versions prior to 2.2, the "aes_gcm_dec" converter and sample_conv_var2smp_str() are implemented in src/ssl_sock.c. Thus the patch will have to be adapted on these versions. Note that this patch is required for a subsequent, more important fix.
2021-02-22 04:03:53 -05:00
/* This function returns a sample struct filled with an <arg> content.
* If the <arg> contains a string, it is returned in the sample flagged as
* SMP_F_CONST. If the <arg> contains a variable descriptor, the sample is
* filled with the content of the variable by using vars_get_by_desc().
*
* Keep in mind that the sample content may be written to a pre-allocated
* trash chunk as returned by get_trash_chunk().
*
* This function returns 0 if an error occurs, otherwise it returns 1.
*/
int sample_conv_var2smp_str(const struct arg *arg, struct sample *smp)
{
switch (arg->type) {
case ARGT_STR:
smp->data.type = SMP_T_STR;
smp->data.u.str = arg->data.str;
BUG/MINOR: sample: secure convs that accept base64 string and var name as args This patch adds a few improvements in order to secure the use of converters that accept base64 string and variable name as arguments. The first change is within related function sample_conv_var2smp_str() which now flags the sample as SMP_F_CONST if the argument is of type ARGT_STR. This makes the sample more safe for later use. A new function sample_check_arg_base64() is added. It checks an argument and fills it with a variable type if the argument string contains a valid variable name. If failed, it tries to perform a base64 decode operation on a non-empty string, and fills the argument with the decoded content which can be used later, without any additional base64dec() function calls during runtime. This means that haproxy configuration check may fail if variable lookup fails and an invalid base64 encoded string is specified as an argument for such converters. Both converters, "aes_gcm_dec" and "hmac", now use alloc_trash_chunk() in order to allocate additional buffers for various conversions, and avoid the use of a pre-allocated trash chunks directly (usually returned by get_trash_chunk()). The function sample_check_arg_base64() is used for both converters in order to check their arguments specified within the haproxy configuration. This patch should be backported as far as 2.0. However, it is important to keep in mind a few things. The "hmac" converter is only available starting with 2.2. In versions prior to 2.2, the "aes_gcm_dec" converter and sample_conv_var2smp_str() are implemented in src/ssl_sock.c. Thus the patch will have to be adapted on these versions. Note that this patch is required for a subsequent, more important fix.
2021-02-22 04:03:53 -05:00
smp->flags = SMP_F_CONST;
return 1;
case ARGT_VAR:
return sample_conv_var2smp(&arg->data.var, smp, SMP_T_STR);
default:
return 0;
}
}
static int sample_conv_2dec_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (args[1].data.sint <= 0 || args[1].data.sint > sizeof(unsigned long long)) {
memprintf(err, "chunk_size out of [1..%u] range (%lld)", (uint)sizeof(unsigned long long), args[1].data.sint);
return 0;
}
if (args[2].data.sint != 0 && args[2].data.sint != 1) {
memprintf(err, "Unsupported truncate value (%lld)", args[2].data.sint);
return 0;
}
return 1;
}
/* Converts big-endian/little-endian binary input sample to a string containing an unsigned
* integer number per <chunk_size> input bytes separated with <separator>.
* Optional <truncate> flag indicates if input is truncated at <chunk_size>
* boundaries.
* Arguments: separator (string), chunk_size (integer), truncate (0,1), big endian (0,1)
*/
static int sample_conv_2dec(const struct arg *args, struct sample *smp, void *private, int be)
{
struct buffer *trash = get_trash_chunk();
const int last = args[2].data.sint ? smp->data.u.str.data - args[1].data.sint + 1 : smp->data.u.str.data;
int max_size = trash->size - 2;
int i;
int start;
int ptr = 0;
unsigned long long number;
char *pos;
trash->data = 0;
while (ptr < last && trash->data <= max_size) {
start = trash->data;
if (ptr) {
/* Add separator */
memcpy(trash->area + trash->data, args[0].data.str.area, args[0].data.str.data);
trash->data += args[0].data.str.data;
}
else
max_size -= args[0].data.str.data;
/* Add integer */
for (number = 0, i = 0; i < args[1].data.sint && ptr < smp->data.u.str.data; i++) {
if (be)
number = (number << 8) + (unsigned char)smp->data.u.str.area[ptr++];
else
number |= (unsigned char)smp->data.u.str.area[ptr++] << (i*8);
}
pos = ulltoa(number, trash->area + trash->data, trash->size - trash->data);
if (pos)
trash->data = pos - trash->area;
else {
trash->data = start;
break;
}
}
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
/* Converts big-endian binary input sample to a string containing an unsigned
* integer number per <chunk_size> input bytes separated with <separator>.
* Optional <truncate> flag indicates if input is truncated at <chunk_size>
* boundaries.
* Arguments: separator (string), chunk_size (integer), truncate (0,1)
*/
static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void *private)
{
return sample_conv_2dec(args, smp, private, 1);
}
/* Converts little-endian binary input sample to a string containing an unsigned
* integer number per <chunk_size> input bytes separated with <separator>.
* Optional <truncate> flag indicates if input is truncated at <chunk_size>
* boundaries.
* Arguments: separator (string), chunk_size (integer), truncate (0,1)
*/
static int sample_conv_le2dec(const struct arg *args, struct sample *smp, void *private)
{
return sample_conv_2dec(args, smp, private, 0);
}
static int sample_conv_be2hex_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (args[1].data.sint <= 0 && (args[0].data.str.data > 0 || args[2].data.sint != 0)) {
memprintf(err, "chunk_size needs to be positive (%lld)", args[1].data.sint);
return 0;
}
if (args[2].data.sint != 0 && args[2].data.sint != 1) {
memprintf(err, "Unsupported truncate value (%lld)", args[2].data.sint);
return 0;
}
return 1;
}
/* Converts big-endian binary input sample to a hex string containing two hex
* digits per input byte. <separator> is put every <chunk_size> binary input
* bytes if specified. Optional <truncate> flag indicates if input is truncated
* at <chunk_size> boundaries.
* Arguments: separator (string), chunk_size (integer), truncate (0,1)
*/
static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
int chunk_size = args[1].data.sint;
const int last = args[2].data.sint ? smp->data.u.str.data - chunk_size + 1 : smp->data.u.str.data;
int i;
int max_size;
int ptr = 0;
unsigned char c;
trash->data = 0;
if (args[0].data.str.data == 0 && args[2].data.sint == 0)
chunk_size = smp->data.u.str.data;
max_size = trash->size - 2 * chunk_size;
while (ptr < last && trash->data <= max_size) {
if (ptr) {
/* Add separator */
memcpy(trash->area + trash->data, args[0].data.str.area, args[0].data.str.data);
trash->data += args[0].data.str.data;
}
else
max_size -= args[0].data.str.data;
/* Add hex */
for (i = 0; i < chunk_size && ptr < smp->data.u.str.data; i++) {
c = smp->data.u.str.area[ptr++];
trash->area[trash->data++] = hextab[(c >> 4) & 0xF];
trash->area[trash->data++] = hextab[c & 0xF];
}
}
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int sample_conv_bin2hex(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
unsigned char c;
int ptr = 0;
trash->data = 0;
while (ptr < smp->data.u.str.data && trash->data <= trash->size - 2) {
c = smp->data.u.str.area[ptr++];
trash->area[trash->data++] = hextab[(c >> 4) & 0xF];
trash->area[trash->data++] = hextab[c & 0xF];
}
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int sample_conv_hex2int(const struct arg *arg_p, struct sample *smp, void *private)
{
long long int n = 0;
int i, c;
for (i = 0; i < smp->data.u.str.data; i++) {
if ((c = hex2i(smp->data.u.str.area[i])) < 0)
return 0;
n = (n << 4) + c;
}
smp->data.u.sint = n;
smp->data.type = SMP_T_SINT;
smp->flags &= ~SMP_F_CONST;
return 1;
}
/* hashes the binary input into a 32-bit unsigned int */
static int sample_conv_djb2(const struct arg *arg_p, struct sample *smp, void *private)
{
smp->data.u.sint = hash_djb2(smp->data.u.str.area,
smp->data.u.str.data);
if (arg_p->data.sint)
smp->data.u.sint = full_hash(smp->data.u.sint);
smp->data.type = SMP_T_SINT;
return 1;
}
static int sample_conv_length(const struct arg *arg_p, struct sample *smp, void *private)
{
int i = smp->data.u.str.data;
smp->data.u.sint = i;
smp->data.type = SMP_T_SINT;
return 1;
}
static int sample_conv_str2lower(const struct arg *arg_p, struct sample *smp, void *private)
{
int i;
if (!smp_make_rw(smp))
return 0;
for (i = 0; i < smp->data.u.str.data; i++) {
if ((smp->data.u.str.area[i] >= 'A') && (smp->data.u.str.area[i] <= 'Z'))
smp->data.u.str.area[i] += 'a' - 'A';
}
return 1;
}
static int sample_conv_str2upper(const struct arg *arg_p, struct sample *smp, void *private)
{
int i;
if (!smp_make_rw(smp))
return 0;
for (i = 0; i < smp->data.u.str.data; i++) {
if ((smp->data.u.str.area[i] >= 'a') && (smp->data.u.str.area[i] <= 'z'))
smp->data.u.str.area[i] += 'A' - 'a';
}
return 1;
}
/* takes the IPv4 mask in args[0] and an optional IPv6 mask in args[1] */
static int sample_conv_ipmask(const struct arg *args, struct sample *smp, void *private)
{
/* Attempt to convert to IPv4 to apply the correct mask. */
c_ipv62ip(smp);
if (smp->data.type == SMP_T_IPV4) {
smp->data.u.ipv4.s_addr &= args[0].data.ipv4.s_addr;
smp->data.type = SMP_T_IPV4;
}
else if (smp->data.type == SMP_T_IPV6) {
/* IPv6 cannot be converted without an IPv6 mask. */
if (args[1].type != ARGT_IPV6)
return 0;
write_u64(&smp->data.u.ipv6.s6_addr[0],
read_u64(&smp->data.u.ipv6.s6_addr[0]) & read_u64(&args[1].data.ipv6.s6_addr[0]));
write_u64(&smp->data.u.ipv6.s6_addr[8],
read_u64(&smp->data.u.ipv6.s6_addr[8]) & read_u64(&args[1].data.ipv6.s6_addr[8]));
smp->data.type = SMP_T_IPV6;
}
return 1;
}
/*
* This function implement a conversion specifier seeker for %N so it could be
* replaced before doing strftime.
*
* <format> is the input format string which is used as a haystack
*
* The function fills multiple variables:
* <skip> is the len of the conversion specifier string which was found (ex: strlen(%N):2, strlen(%3N):3 strlen(%123N): 5)
* <width> is the width argument, default width is 9 (ex: %3N: 3, %4N: 4: %N: 9, %5N: 5)
*
* Returns a ptr to the first character of the conversion specifier or NULL if not found
*/
static const char *lookup_convspec_N(const char *format, int *skip, int *width)
{
const char *p, *needle;
const char *digits;
int state;
p = format;
/* this looks for % in loop. The iteration stops when a %N conversion
* specifier was found or there is no '%' anymore */
lookagain:
while (p && *p) {
state = 0;
digits = NULL;
p = needle = strchr(p, '%');
/* Once we find a % we try to move forward in the string
*
* state 0: found %
* state 1: digits (precision)
* state 2: N
*/
while (p && *p) {
switch (state) {
case 0:
state = 1;
break;
case 1:
if (isdigit((unsigned char)*p) && !digits) /* set the start of the digits */
digits = p;
if (isdigit((unsigned char)*p))
break;
else
state = 2;
/* if this is not a number anymore, we
* don't want to increment p but try the
* next state directly */
__fallthrough;
case 2:
if (*p == 'N')
goto found;
else
/* this was not a %N, start again */
goto lookagain;
break;
}
p++;
}
}
*skip = 0;
*width = 0;
return NULL;
found:
*skip = p - needle + 1;
if (digits)
*width = atoi(digits);
else
*width = 9;
return needle;
}
/*
* strftime(3) does not implement nanoseconds, but we still want them in our
* date format.
*
* This function implements %N like in date(1) which gives you the nanoseconds part of the timestamp
* An optional field width can be specified, a maximum width of 9 is supported (ex: %3N %6N %9N)
*
* <format> is the format string
* <curr_date> in seconds since epoch
* <ns> only the nanoseconds part of the timestamp
* <local> chose the localtime instead of UTC time
*
* Return the results of strftime in the trash buffer
*/
static struct buffer *conv_time_common(const char *format, time_t curr_date, uint64_t ns, int local)
{
struct buffer *tmp_format = NULL;
struct buffer *res = NULL;
struct tm tm;
const char *p;
char ns_str[10] = {};
int set = 0;
if (local)
get_localtime(curr_date, &tm);
else
get_gmtime(curr_date, &tm);
/* we need to iterate in order to replace all the %N in the string */
p = format;
while (*p) {
const char *needle;
int skip = 0;
int cpy = 0;
int width = 0;
/* look for the next %N onversion specifier */
if (!(needle = lookup_convspec_N(p, &skip, &width)))
break;
if (width > 9) /* we don't handle more that 9 */
width = 9;
cpy = needle - p;
if (!tmp_format)
tmp_format = alloc_trash_chunk();
if (!tmp_format)
goto error;
if (set != 9) /* if the snprintf wasn't done yet */
set = snprintf(ns_str, sizeof(ns_str), "%.9llu", (unsigned long long)ns);
if (chunk_istcat(tmp_format, ist2(p, cpy)) == 0) /* copy before the %N */
goto error;
if (chunk_istcat(tmp_format, ist2(ns_str, width)) == 0) /* copy the %N result with the right precision */
goto error;
p += skip + cpy; /* skip the %N */
}
if (tmp_format) { /* %N was found */
if (chunk_strcat(tmp_format, p) == 0) /* copy the end of the string if needed or just the \0 */
goto error;
res = get_trash_chunk();
res->data = strftime(res->area, res->size, tmp_format->area , &tm);
} else {
res = get_trash_chunk();
res->data = strftime(res->area, res->size, format, &tm);
}
error:
free_trash_chunk(tmp_format);
return res;
}
/*
* same as sample_conv_ltime but input is us and %N is supported
*/
static int sample_conv_us_ltime(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *temp;
time_t curr_date = smp->data.u.sint / 1000000; /* convert us to s */
uint64_t ns = (smp->data.u.sint % 1000000) * 1000; /* us part to ns */
/* add offset */
if (args[1].type == ARGT_SINT)
curr_date += args[1].data.sint;
temp = conv_time_common(args[0].data.str.area, curr_date, ns, 1);
smp->data.u.str = *temp;
smp->data.type = SMP_T_STR;
return 1;
}
/*
* same as sample_conv_ltime but input is ms and %N is supported
*/
static int sample_conv_ms_ltime(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *temp;
time_t curr_date = smp->data.u.sint / 1000; /* convert ms to s */
uint64_t ns = (smp->data.u.sint % 1000) * 1000000; /* ms part to ns */
/* add offset */
if (args[1].type == ARGT_SINT)
curr_date += args[1].data.sint;
temp = conv_time_common(args[0].data.str.area, curr_date, ns, 1);
smp->data.u.str = *temp;
smp->data.type = SMP_T_STR;
return 1;
}
/* takes an UINT value on input supposed to represent the time since EPOCH,
* adds an optional offset found in args[1] and emits a string representing
* the local time in the format specified in args[1] using strftime().
*/
static int sample_conv_ltime(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *temp;
/* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
struct tm tm;
/* add offset */
if (args[1].type == ARGT_SINT)
curr_date += args[1].data.sint;
get_localtime(curr_date, &tm);
temp = get_trash_chunk();
temp->data = strftime(temp->area, temp->size, args[0].data.str.area, &tm);
smp->data.u.str = *temp;
smp->data.type = SMP_T_STR;
return 1;
}
/* hashes the binary input into a 32-bit unsigned int */
static int sample_conv_sdbm(const struct arg *arg_p, struct sample *smp, void *private)
{
smp->data.u.sint = hash_sdbm(smp->data.u.str.area,
smp->data.u.str.data);
if (arg_p->data.sint)
smp->data.u.sint = full_hash(smp->data.u.sint);
smp->data.type = SMP_T_SINT;
return 1;
}
/*
* same as sample_conv_utime but input is us and %N is supported
*/
static int sample_conv_us_utime(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *temp;
time_t curr_date = smp->data.u.sint / 1000000; /* convert us to s */
uint64_t ns = (smp->data.u.sint % 1000000) * 1000; /* us part to ns */
/* add offset */
if (args[1].type == ARGT_SINT)
curr_date += args[1].data.sint;
temp = conv_time_common(args[0].data.str.area, curr_date, ns, 0);
smp->data.u.str = *temp;
smp->data.type = SMP_T_STR;
return 1;
}
/*
* same as sample_conv_utime but input is ms and %N is supported
*/
static int sample_conv_ms_utime(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *temp;
time_t curr_date = smp->data.u.sint / 1000; /* convert ms to s */
uint64_t ns = (smp->data.u.sint % 1000) * 1000000; /* ms part to ns */
/* add offset */
if (args[1].type == ARGT_SINT)
curr_date += args[1].data.sint;
temp = conv_time_common(args[0].data.str.area, curr_date, ns, 0);
smp->data.u.str = *temp;
smp->data.type = SMP_T_STR;
return 1;
}
/* takes an UINT value on input supposed to represent the time since EPOCH,
* adds an optional offset found in args[1] and emits a string representing
* the UTC date in the format specified in args[1] using strftime().
*/
static int sample_conv_utime(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *temp;
/* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
struct tm tm;
/* add offset */
if (args[1].type == ARGT_SINT)
curr_date += args[1].data.sint;
get_gmtime(curr_date, &tm);
temp = get_trash_chunk();
temp->data = strftime(temp->area, temp->size, args[0].data.str.area, &tm);
smp->data.u.str = *temp;
smp->data.type = SMP_T_STR;
return 1;
}
/* hashes the binary input into a 32-bit unsigned int */
static int sample_conv_wt6(const struct arg *arg_p, struct sample *smp, void *private)
{
smp->data.u.sint = hash_wt6(smp->data.u.str.area,
smp->data.u.str.data);
if (arg_p->data.sint)
smp->data.u.sint = full_hash(smp->data.u.sint);
smp->data.type = SMP_T_SINT;
return 1;
}
/* hashes the binary input into a 32-bit unsigned int using xxh.
* The seed of the hash defaults to 0 but can be changd in argument 1.
*/
static int sample_conv_xxh32(const struct arg *arg_p, struct sample *smp, void *private)
{
unsigned int seed;
if (arg_p->data.sint)
seed = arg_p->data.sint;
else
seed = 0;
smp->data.u.sint = XXH32(smp->data.u.str.area, smp->data.u.str.data,
seed);
smp->data.type = SMP_T_SINT;
return 1;
}
/* hashes the binary input into a 64-bit unsigned int using xxh.
* In fact, the function returns a 64 bit unsigned, but the sample
* storage of haproxy only proposes 64-bits signed, so the value is
* cast as signed. This cast doesn't impact the hash repartition.
* The seed of the hash defaults to 0 but can be changd in argument 1.
*/
static int sample_conv_xxh64(const struct arg *arg_p, struct sample *smp, void *private)
{
unsigned long long int seed;
if (arg_p->data.sint)
seed = (unsigned long long int)arg_p->data.sint;
else
seed = 0;
smp->data.u.sint = (long long int)XXH64(smp->data.u.str.area,
smp->data.u.str.data, seed);
smp->data.type = SMP_T_SINT;
return 1;
}
static int sample_conv_xxh3(const struct arg *arg_p, struct sample *smp, void *private)
{
unsigned long long int seed;
if (arg_p->data.sint)
seed = (unsigned long long int)arg_p->data.sint;
else
seed = 0;
smp->data.u.sint = (long long int)XXH3(smp->data.u.str.area,
smp->data.u.str.data, seed);
smp->data.type = SMP_T_SINT;
return 1;
}
/* hashes the binary input into a 32-bit unsigned int */
static int sample_conv_crc32(const struct arg *arg_p, struct sample *smp, void *private)
{
smp->data.u.sint = hash_crc32(smp->data.u.str.area,
smp->data.u.str.data);
if (arg_p->data.sint)
smp->data.u.sint = full_hash(smp->data.u.sint);
smp->data.type = SMP_T_SINT;
return 1;
}
/* hashes the binary input into crc32c (RFC4960, Appendix B [8].) */
static int sample_conv_crc32c(const struct arg *arg_p, struct sample *smp, void *private)
{
smp->data.u.sint = hash_crc32c(smp->data.u.str.area,
smp->data.u.str.data);
if (arg_p->data.sint)
smp->data.u.sint = full_hash(smp->data.u.sint);
smp->data.type = SMP_T_SINT;
return 1;
}
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
/* This function escape special json characters. The returned string can be
* safely set between two '"' and used as json string. The json string is
* defined like this:
*
* any Unicode character except '"' or '\' or control character
* \", \\, \/, \b, \f, \n, \r, \t, \u + four-hex-digits
*
* The enum input_type contain all the allowed mode for decoding the input
* string.
*/
enum input_type {
IT_ASCII = 0,
IT_UTF8,
IT_UTF8S,
IT_UTF8P,
IT_UTF8PS,
};
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
static int sample_conv_json_check(struct arg *arg, struct sample_conv *conv,
const char *file, int line, char **err)
{
enum input_type type;
if (strcmp(arg->data.str.area, "") == 0)
type = IT_ASCII;
else if (strcmp(arg->data.str.area, "ascii") == 0)
type = IT_ASCII;
else if (strcmp(arg->data.str.area, "utf8") == 0)
type = IT_UTF8;
else if (strcmp(arg->data.str.area, "utf8s") == 0)
type = IT_UTF8S;
else if (strcmp(arg->data.str.area, "utf8p") == 0)
type = IT_UTF8P;
else if (strcmp(arg->data.str.area, "utf8ps") == 0)
type = IT_UTF8PS;
else {
memprintf(err, "Unexpected input code type. "
"Allowed value are 'ascii', 'utf8', 'utf8s', 'utf8p' and 'utf8ps'");
return 0;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
}
chunk_destroy(&arg->data.str);
arg->type = ARGT_SINT;
arg->data.sint = type;
return 1;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
}
static int sample_conv_json(const struct arg *arg_p, struct sample *smp, void *private)
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
{
struct buffer *temp;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
char _str[7]; /* \u + 4 hex digit + null char for sprintf. */
const char *str;
int len;
enum input_type input_type = IT_ASCII;
unsigned int c;
unsigned int ret;
char *p;
input_type = arg_p->data.sint;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
temp = get_trash_chunk();
temp->data = 0;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
p = smp->data.u.str.area;
while (p < smp->data.u.str.area + smp->data.u.str.data) {
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
if (input_type == IT_ASCII) {
/* Read input as ASCII. */
c = *(unsigned char *)p;
p++;
}
else {
/* Read input as UTF8. */
ret = utf8_next(p,
smp->data.u.str.data - ( p - smp->data.u.str.area),
&c);
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
p += utf8_return_length(ret);
if (input_type == IT_UTF8 && utf8_return_code(ret) != UTF8_CODE_OK)
return 0;
if (input_type == IT_UTF8S && utf8_return_code(ret) != UTF8_CODE_OK)
continue;
if (input_type == IT_UTF8P && utf8_return_code(ret) & (UTF8_CODE_INVRANGE|UTF8_CODE_BADSEQ))
return 0;
if (input_type == IT_UTF8PS && utf8_return_code(ret) & (UTF8_CODE_INVRANGE|UTF8_CODE_BADSEQ))
continue;
/* Check too big values. */
if ((unsigned int)c > 0xffff) {
if (input_type == IT_UTF8 || input_type == IT_UTF8P)
return 0;
continue;
}
}
/* Convert character. */
if (c == '"') {
len = 2;
str = "\\\"";
}
else if (c == '\\') {
len = 2;
str = "\\\\";
}
else if (c == '/') {
len = 2;
str = "\\/";
}
else if (c == '\b') {
len = 2;
str = "\\b";
}
else if (c == '\f') {
len = 2;
str = "\\f";
}
else if (c == '\r') {
len = 2;
str = "\\r";
}
else if (c == '\n') {
len = 2;
str = "\\n";
}
else if (c == '\t') {
len = 2;
str = "\\t";
}
else if (c > 0xff || !isprint((unsigned char)c)) {
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
/* isprint generate a segfault if c is too big. The man says that
* c must have the value of an unsigned char or EOF.
*/
len = 6;
_str[0] = '\\';
_str[1] = 'u';
snprintf(&_str[2], 5, "%04x", (unsigned short)c);
str = _str;
}
else {
len = 1;
_str[0] = c;
str = _str;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
}
/* Check length */
if (temp->data + len > temp->size)
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
return 0;
/* Copy string. */
memcpy(temp->area + temp->data, str, len);
temp->data += len;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
}
smp->flags &= ~SMP_F_CONST;
smp->data.u.str = *temp;
smp->data.type = SMP_T_STR;
MINOR: sample: add "json" converter This converter escapes string to use it as json/ascii escaped string. It can read UTF-8 with differents behavior on errors and encode it in json/ascii. json([<input-code>]) Escapes the input string and produces an ASCII ouput string ready to use as a JSON string. The converter tries to decode the input string according to the <input-code> parameter. It can be "ascii", "utf8", "utf8s", "utf8"" or "utf8ps". The "ascii" decoder never fails. The "utf8" decoder detects 3 types of errors: - bad UTF-8 sequence (lone continuation byte, bad number of continuation bytes, ...) - invalid range (the decoded value is within a UTF-8 prohibited range), - code overlong (the value is encoded with more bytes than necessary). The UTF-8 JSON encoding can produce a "too long value" error when the UTF-8 character is greater than 0xffff because the JSON string escape specification only authorizes 4 hex digits for the value encoding. The UTF-8 decoder exists in 4 variants designated by a combination of two suffix letters : "p" for "permissive" and "s" for "silently ignore". The behaviors of the decoders are : - "ascii" : never fails ; - "utf8" : fails on any detected errors ; - "utf8s" : never fails, but removes characters corresponding to errors ; - "utf8p" : accepts and fixes the overlong errors, but fails on any other error ; - "utf8ps" : never fails, accepts and fixes the overlong errors, but removes characters corresponding to the other errors. This converter is particularly useful for building properly escaped JSON for logging to servers which consume JSON-formated traffic logs. Example: capture request header user-agent len 150 capture request header Host len 15 log-format {"ip":"%[src]","user-agent":"%[capture.req.hdr(1),json]"} Input request from client 127.0.0.1: GET / HTTP/1.0 User-Agent: Very "Ugly" UA 1/2 Output log: {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
2014-08-12 04:20:47 -04:00
return 1;
}
/* This sample function is designed to extract some bytes from an input buffer.
* First arg is the offset.
* Optional second arg is the length to truncate */
static int sample_conv_bytes(const struct arg *arg_p, struct sample *smp, void *private)
{
struct sample smp_arg0, smp_arg1;
long long start_idx, length;
// determine the start_idx and length of the output
smp_set_owner(&smp_arg0, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(&arg_p[0], &smp_arg0) || smp_arg0.data.u.sint < 0) {
/* invalid or negative value */
goto fail;
}
if (smp_arg0.data.u.sint >= smp->data.u.str.data) {
// arg0 >= the input length
if (smp->opt & SMP_OPT_FINAL) {
// empty output value on final smp
smp->data.u.str.data = 0;
goto end;
}
goto wait;
}
start_idx = smp_arg0.data.u.sint;
// length comes from arg1 if present, otherwise it's the remaining length
length = smp->data.u.str.data - start_idx;
if (arg_p[1].type != ARGT_STOP) {
smp_set_owner(&smp_arg1, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(&arg_p[1], &smp_arg1) || smp_arg1.data.u.sint < 0) {
// invalid or negative value
goto fail;
}
if (smp_arg1.data.u.sint > (smp->data.u.str.data - start_idx)) {
// arg1 value is greater than the remaining length
if (smp->opt & SMP_OPT_FINAL) {
// truncate to remaining length
length = smp->data.u.str.data - start_idx;
goto end;
}
goto wait;
}
length = smp_arg1.data.u.sint;
}
// update the output using the start_idx and length
smp->data.u.str.area += start_idx;
smp->data.u.str.data = length;
end:
return 1;
fail:
smp->flags &= ~SMP_F_MAY_CHANGE;
wait:
smp->data.u.str.data = 0;
return 0;
}
static int sample_conv_field_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
struct arg *arg = args;
if (arg->type != ARGT_SINT) {
memprintf(err, "Unexpected arg type");
return 0;
}
if (!arg->data.sint) {
memprintf(err, "Unexpected value 0 for index");
return 0;
}
arg++;
if (arg->type != ARGT_STR) {
memprintf(err, "Unexpected arg type");
return 0;
}
if (!arg->data.str.data) {
memprintf(err, "Empty separators list");
return 0;
}
return 1;
}
/* This sample function is designed to a return selected part of a string (field).
* First arg is the index of the field (start at 1)
* Second arg is a char list of separators (type string)
*/
static int sample_conv_field(const struct arg *arg_p, struct sample *smp, void *private)
{
int field;
char *start, *end;
int i;
int count = (arg_p[2].type == ARGT_SINT) ? arg_p[2].data.sint : 1;
if (!arg_p[0].data.sint)
return 0;
if (arg_p[0].data.sint < 0) {
field = -1;
end = start = smp->data.u.str.area + smp->data.u.str.data;
while (start > smp->data.u.str.area) {
for (i = 0 ; i < arg_p[1].data.str.data; i++) {
if (*(start-1) == arg_p[1].data.str.area[i]) {
if (field == arg_p[0].data.sint) {
if (count == 1)
goto found;
else if (count > 1)
count--;
} else {
end = start-1;
field--;
}
break;
}
}
start--;
}
} else {
field = 1;
end = start = smp->data.u.str.area;
while (end - smp->data.u.str.area < smp->data.u.str.data) {
for (i = 0 ; i < arg_p[1].data.str.data; i++) {
if (*end == arg_p[1].data.str.area[i]) {
if (field == arg_p[0].data.sint) {
if (count == 1)
goto found;
else if (count > 1)
count--;
} else {
start = end+1;
field++;
}
break;
}
}
end++;
}
}
/* Field not found */
if (field != arg_p[0].data.sint) {
smp->data.u.str.data = 0;
return 0;
}
found:
smp->data.u.str.data = end - start;
/* If ret string is len 0, no need to
change pointers or to update size */
if (!smp->data.u.str.data)
return 1;
/* Compute remaining size if needed
Note: smp->data.u.str.size cannot be set to 0 */
if (smp->data.u.str.size)
smp->data.u.str.size -= start - smp->data.u.str.area;
smp->data.u.str.area = start;
return 1;
}
/* This sample function is designed to return a word from a string.
* First arg is the index of the word (start at 1)
* Second arg is a char list of words separators (type string)
*/
static int sample_conv_word(const struct arg *arg_p, struct sample *smp, void *private)
{
int word;
char *start, *end;
int i, issep, inword;
int count = (arg_p[2].type == ARGT_SINT) ? arg_p[2].data.sint : 1;
if (!arg_p[0].data.sint)
return 0;
word = 0;
inword = 0;
if (arg_p[0].data.sint < 0) {
end = start = smp->data.u.str.area + smp->data.u.str.data;
while (start > smp->data.u.str.area) {
issep = 0;
for (i = 0 ; i < arg_p[1].data.str.data; i++) {
if (*(start-1) == arg_p[1].data.str.area[i]) {
issep = 1;
break;
}
}
if (!inword) {
if (!issep) {
if (word != arg_p[0].data.sint) {
word--;
end = start;
}
inword = 1;
}
}
else if (issep) {
if (word == arg_p[0].data.sint) {
if (count == 1)
goto found;
else if (count > 1)
count--;
}
inword = 0;
}
start--;
}
} else {
end = start = smp->data.u.str.area;
while (end - smp->data.u.str.area < smp->data.u.str.data) {
issep = 0;
for (i = 0 ; i < arg_p[1].data.str.data; i++) {
if (*end == arg_p[1].data.str.area[i]) {
issep = 1;
break;
}
}
if (!inword) {
if (!issep) {
if (word != arg_p[0].data.sint) {
word++;
start = end;
}
inword = 1;
}
}
else if (issep) {
if (word == arg_p[0].data.sint) {
if (count == 1)
goto found;
else if (count > 1)
count--;
}
inword = 0;
}
end++;
}
}
/* Field not found */
if (word != arg_p[0].data.sint) {
smp->data.u.str.data = 0;
return 0;
}
found:
smp->data.u.str.data = end - start;
/* If ret string is len 0, no need to
change pointers or to update size */
if (!smp->data.u.str.data)
return 1;
/* Compute remaining size if needed
Note: smp->data.u.str.size cannot be set to 0 */
if (smp->data.u.str.size)
smp->data.u.str.size -= start - smp->data.u.str.area;
smp->data.u.str.area = start;
return 1;
}
static int sample_conv_param_check(struct arg *arg, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (arg[1].type == ARGT_STR && arg[1].data.str.data != 1) {
memprintf(err, "Delimiter must be exactly 1 character.");
return 0;
}
return 1;
}
static int sample_conv_param(const struct arg *arg_p, struct sample *smp, void *private)
{
char *pos, *end, *pend, *equal;
char delim = '&';
const char *name = arg_p[0].data.str.area;
size_t name_l = arg_p[0].data.str.data;
if (arg_p[1].type == ARGT_STR)
delim = *arg_p[1].data.str.area;
pos = smp->data.u.str.area;
end = pos + smp->data.u.str.data;
while (pos < end) {
equal = pos + name_l;
/* Parameter not found */
if (equal > end)
break;
if (equal == end || *equal == delim) {
if (memcmp(pos, name, name_l) == 0) {
/* input contains parameter, but no value is supplied */
smp->data.u.str.data = 0;
return 1;
}
pos = equal + 1;
continue;
}
if (*equal == '=' && memcmp(pos, name, name_l) == 0) {
pos = equal + 1;
pend = memchr(pos, delim, end - pos);
if (pend == NULL)
pend = end;
if (smp->data.u.str.size)
smp->data.u.str.size -= pos - smp->data.u.str.area;
smp->data.u.str.area = pos;
smp->data.u.str.data = pend - pos;
return 1;
}
/* find the next delimiter and set position to character after that */
pos = memchr(pos, delim, end - pos);
if (pos == NULL)
pos = end;
else
pos++;
}
/* Parameter not found */
smp->data.u.str.data = 0;
return 0;
}
static int sample_conv_regsub_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
struct arg *arg = args;
char *p;
int len;
/* arg0 is a regex, it uses type_flag for ICASE and global match */
arg[0].type_flags = 0;
if (arg[2].type != ARGT_STR)
return 1;
p = arg[2].data.str.area;
len = arg[2].data.str.data;
while (len) {
if (*p == 'i') {
arg[0].type_flags |= ARGF_REG_ICASE;
}
else if (*p == 'g') {
arg[0].type_flags |= ARGF_REG_GLOB;
}
else {
memprintf(err, "invalid regex flag '%c', only 'i' and 'g' are supported", *p);
return 0;
}
p++;
len--;
}
return 1;
}
/* This sample function is designed to do the equivalent of s/match/replace/ on
* the input string. It applies a regex and restarts from the last matched
* location until nothing matches anymore. First arg is the regex to apply to
* the input string, second arg is the replacement expression.
*/
static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void *private)
{
char *start, *end;
struct my_regex *reg = arg_p[0].data.reg;
regmatch_t pmatch[MAX_MATCH];
struct buffer *trash = get_trash_chunk();
struct buffer *output;
int flag, max;
int found;
start = smp->data.u.str.area;
end = start + smp->data.u.str.data;
flag = 0;
while (1) {
/* check for last round which is used to copy remaining parts
* when not running in global replacement mode.
*/
found = 0;
if ((arg_p[0].type_flags & ARGF_REG_GLOB) || !(flag & REG_NOTBOL)) {
/* Note: we can have start == end on empty strings or at the end */
found = regex_exec_match2(reg, start, end - start, MAX_MATCH, pmatch, flag);
}
if (!found)
pmatch[0].rm_so = end - start;
/* copy the heading non-matching part (which may also be the tail if nothing matches) */
max = trash->size - trash->data;
if (max && pmatch[0].rm_so > 0) {
if (max > pmatch[0].rm_so)
max = pmatch[0].rm_so;
memcpy(trash->area + trash->data, start, max);
trash->data += max;
}
if (!found)
break;
output = alloc_trash_chunk();
if (!output)
break;
output->data = exp_replace(output->area, output->size, start, arg_p[1].data.str.area, pmatch);
/* replace the matching part */
BUG/MEDIUM: sample: fix risk of overflow when replacing multiple regex back-refs Aleandro Prudenzano of Doyensec and Edoardo Geraci of Codean Labs reported a bug in sample_conv_regsub(), which can cause replacements of multiple back-references to overflow the temporary trash buffer. The problem happens when doing "regsub(match,replacement,g)": we're replacing every occurrence of "match" with "replacement" in the input sample, which requires a length check. For this, a max is applied, so that a replacement may not use more than the remaining length in the buffer. However, the length check is made on the replaced pattern and not on the temporary buffer used to carry the new string. This results in the remaining size to be usable for each input match, which can go beyond the temporary buffer size if more than one occurrence has to be replaced with something that's larger than the remaining room. The fix proposed by Aleandro and Edoardo is the correct one (check on "trash" not "output"), and is the one implemented in this patch. While it is very unlikely that a config will replace multiple short patterns each with a larger one in a request, this possibility cannot be entirely ruled out (e.g. mask a known, short IP address using "XXX.XXX.XXX.XXX"). However when this happens, the replacement pattern will be static, and not be user-controlled, which is why this patch is marked as medium. The bug was introduced in 2.2 with commit 07e1e3c93e ("MINOR: sample: regsub now supports backreferences"), so it must be backported to all versions. Special thanks go to Aleandro and Edoardo for reporting this bug with a simple reproducer and a fix.
2025-04-07 09:30:43 -04:00
max = trash->size - trash->data;
if (max) {
if (max > output->data)
max = output->data;
memcpy(trash->area + trash->data,
output->area, max);
trash->data += max;
}
free_trash_chunk(output);
/* stop here if we're done with this string */
if (start >= end)
break;
/* We have a special case for matches of length 0 (eg: "x*y*").
* These ones are considered to match in front of a character,
* so we have to copy that character and skip to the next one.
*/
if (!pmatch[0].rm_eo) {
if (trash->data < trash->size)
trash->area[trash->data++] = start[pmatch[0].rm_eo];
pmatch[0].rm_eo++;
}
start += pmatch[0].rm_eo;
flag |= REG_NOTBOL;
}
smp->data.u.str = *trash;
return 1;
}
/* This function check an operator entry. It expects a string.
* The string can be an integer or a variable name.
*/
static int check_operator(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
const char *str;
const char *end;
long long int i;
/* Try to decode a variable. The 'err' variable is intentionnaly left
* NULL since the operators accept an integer as argument in which case
* vars_check_arg call will fail.
*/
if (vars_check_arg(&args[0], NULL))
return 1;
/* Try to convert an integer */
str = args[0].data.str.area;
end = str + strlen(str);
i = read_int64(&str, end);
if (*str != '\0') {
memprintf(err, "expects an integer or a variable name");
return 0;
}
chunk_destroy(&args[0].data.str);
args[0].type = ARGT_SINT;
args[0].data.sint = i;
return 1;
}
/* This function returns a sample struct filled with an arg content.
* If the arg contain an integer, the integer is returned in the
* sample. If the arg contains a variable descriptor, it returns the
* variable value.
*
* This function returns 0 if an error occurs, otherwise it returns 1.
*/
int sample_conv_var2smp_sint(const struct arg *arg, struct sample *smp)
{
switch (arg->type) {
case ARGT_SINT:
smp->data.type = SMP_T_SINT;
smp->data.u.sint = arg->data.sint;
return 1;
case ARGT_VAR:
return sample_conv_var2smp(&arg->data.var, smp, SMP_T_SINT);
default:
return 0;
}
}
/* Takes a SINT on input, applies a binary twos complement and returns the SINT
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
* result.
*/
static int sample_conv_binary_cpl(const struct arg *arg_p, struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
smp->data.u.sint = ~smp->data.u.sint;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, applies a binary "and" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_binary_and(const struct arg *arg_p, struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
smp->data.u.sint &= tmp.data.u.sint;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, applies a binary "or" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_binary_or(const struct arg *arg_p, struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
smp->data.u.sint |= tmp.data.u.sint;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, applies a binary "xor" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_binary_xor(const struct arg *arg_p, struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
smp->data.u.sint ^= tmp.data.u.sint;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
static inline long long int arith_add(long long int a, long long int b)
{
/* Prevent overflow and makes capped calculus.
* We must ensure that the check calculus doesn't
* exceed the signed 64 bits limits.
*
* +----------+----------+
* | a<0 | a>=0 |
* +------+----------+----------+
* | b<0 | MIN-a>b | no check |
* +------+----------+----------+
* | b>=0 | no check | MAX-a<b |
* +------+----------+----------+
*/
if ((a ^ b) >= 0) {
/* signs are same. */
if (a < 0) {
if (LLONG_MIN - a > b)
return LLONG_MIN;
}
else if (LLONG_MAX - a < b)
return LLONG_MAX;
}
return a + b;
}
/* Takes a SINT on input, applies an arithmetic "add" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_arith_add(const struct arg *arg_p, struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
smp->data.u.sint = arith_add(smp->data.u.sint, tmp.data.u.sint);
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, applies an arithmetic "sub" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_arith_sub(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
/* We cannot represent -LLONG_MIN because abs(LLONG_MIN) is greater
* than abs(LLONG_MAX). So, the following code use LLONG_MAX in place
* of -LLONG_MIN and correct the result.
*/
if (tmp.data.u.sint == LLONG_MIN) {
smp->data.u.sint = arith_add(smp->data.u.sint, LLONG_MAX);
if (smp->data.u.sint < LLONG_MAX)
smp->data.u.sint++;
return 1;
}
/* standard subtraction: we use the "add" function and negate
* the second operand.
*/
smp->data.u.sint = arith_add(smp->data.u.sint, -tmp.data.u.sint);
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, applies an arithmetic "mul" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
* If the result makes an overflow, then the largest possible quantity is
* returned.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_arith_mul(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
long long int c;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
/* prevent divide by 0 during the check */
if (!smp->data.u.sint || !tmp.data.u.sint) {
smp->data.u.sint = 0;
return 1;
}
/* The multiply between LLONG_MIN and -1 returns a
* "floating point exception".
*/
if (smp->data.u.sint == LLONG_MIN && tmp.data.u.sint == -1) {
smp->data.u.sint = LLONG_MAX;
return 1;
}
/* execute standard multiplication. */
c = smp->data.u.sint * tmp.data.u.sint;
/* check for overflow and makes capped multiply. */
if (smp->data.u.sint != c / tmp.data.u.sint) {
if ((smp->data.u.sint < 0) == (tmp.data.u.sint < 0)) {
smp->data.u.sint = LLONG_MAX;
return 1;
}
smp->data.u.sint = LLONG_MIN;
return 1;
}
smp->data.u.sint = c;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, applies an arithmetic "div" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
* If arg_p makes the result overflow, then the largest possible quantity is
* returned.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_arith_div(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
if (tmp.data.u.sint) {
/* The divide between LLONG_MIN and -1 returns a
* "floating point exception".
*/
if (smp->data.u.sint == LLONG_MIN && tmp.data.u.sint == -1) {
smp->data.u.sint = LLONG_MAX;
return 1;
}
smp->data.u.sint /= tmp.data.u.sint;
return 1;
}
smp->data.u.sint = LLONG_MAX;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, applies an arithmetic "mod" with the SINT directly in
* arg_p or in the variable described in arg_p, and returns the SINT result.
* If arg_p makes the result overflow, then 0 is returned.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
*/
static int sample_conv_arith_mod(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
struct sample tmp;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_sint(arg_p, &tmp))
return 0;
if (tmp.data.u.sint) {
/* The divide between LLONG_MIN and -1 returns a
* "floating point exception".
*/
if (smp->data.u.sint == LLONG_MIN && tmp.data.u.sint == -1) {
smp->data.u.sint = 0;
return 1;
}
smp->data.u.sint %= tmp.data.u.sint;
return 1;
}
smp->data.u.sint = 0;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes an SINT on input, applies an arithmetic "neg" and returns the SINT
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
* result.
*/
static int sample_conv_arith_neg(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
if (smp->data.u.sint == LLONG_MIN)
smp->data.u.sint = LLONG_MAX;
else
smp->data.u.sint = -smp->data.u.sint;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, returns true is the value is non-null, otherwise
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
* false. The output is a BOOL.
*/
static int sample_conv_arith_bool(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
smp->data.u.sint = !!smp->data.u.sint;
smp->data.type = SMP_T_BOOL;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, returns false is the value is non-null, otherwise
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
* truee. The output is a BOOL.
*/
static int sample_conv_arith_not(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
smp->data.u.sint = !smp->data.u.sint;
smp->data.type = SMP_T_BOOL;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, returns true is the value is odd, otherwise false.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
* The output is a BOOL.
*/
static int sample_conv_arith_odd(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
smp->data.u.sint = smp->data.u.sint & 1;
smp->data.type = SMP_T_BOOL;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* Takes a SINT on input, returns true is the value is even, otherwise false.
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
* The output is a BOOL.
*/
static int sample_conv_arith_even(const struct arg *arg_p,
struct sample *smp, void *private)
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{
smp->data.u.sint = !(smp->data.u.sint & 1);
smp->data.type = SMP_T_BOOL;
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
return 1;
}
/* appends an optional const string, an optional variable contents and another
* optional const string to an existing string.
*/
static int sample_conv_concat(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash;
struct sample tmp;
int max;
trash = alloc_trash_chunk();
if (!trash)
return 0;
trash->data = smp->data.u.str.data;
if (trash->data > trash->size - 1)
trash->data = trash->size - 1;
memcpy(trash->area, smp->data.u.str.area, trash->data);
trash->area[trash->data] = 0;
/* append first string */
max = arg_p[0].data.str.data;
if (max > trash->size - 1 - trash->data)
max = trash->size - 1 - trash->data;
if (max) {
memcpy(trash->area + trash->data, arg_p[0].data.str.area, max);
trash->data += max;
trash->area[trash->data] = 0;
}
/* append second string (variable) if it's found and we can turn it
* into a string.
*/
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) &&
(sample_casts[tmp.data.type][SMP_T_STR] == c_none ||
sample_casts[tmp.data.type][SMP_T_STR](&tmp))) {
max = tmp.data.u.str.data;
if (max > trash->size - 1 - trash->data)
max = trash->size - 1 - trash->data;
if (max) {
memcpy(trash->area + trash->data, tmp.data.u.str.area,
max);
trash->data += max;
trash->area[trash->data] = 0;
}
}
/* append third string */
max = arg_p[2].data.str.data;
if (max > trash->size - 1 - trash->data)
max = trash->size - 1 - trash->data;
if (max) {
memcpy(trash->area + trash->data, arg_p[2].data.str.area, max);
trash->data += max;
trash->area[trash->data] = 0;
}
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp_dup(smp);
free_trash_chunk(trash);
return 1;
}
/* This function checks the "concat" converter's arguments and extracts the
* variable name and its scope.
*/
static int smp_check_concat(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
/* Try to decode a variable. */
if (args[1].data.str.data > 0 && !vars_check_arg(&args[1], NULL)) {
memprintf(err, "failed to register variable name '%s'",
args[1].data.str.area);
return 0;
}
return 1;
}
/* Append delimiter (only to a non empty input) followed by the optional
* variable contents concatenated with the optional suffix.
*/
static int sample_conv_add_item(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *tmpbuf;
struct sample tmp;
size_t max;
int var_available;
tmpbuf = alloc_trash_chunk();
if (!tmpbuf)
return 0;
tmpbuf->data = smp->data.u.str.data;
if (tmpbuf->data > tmpbuf->size - 1)
tmpbuf->data = tmpbuf->size - 1;
memcpy(tmpbuf->area, smp->data.u.str.area, tmpbuf->data);
tmpbuf->area[tmpbuf->data] = 0;
/* Check if variable is found and we can turn into a string. */
var_available = 0;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) &&
(sample_casts[tmp.data.type][SMP_T_STR] == c_none ||
sample_casts[tmp.data.type][SMP_T_STR](&tmp)))
var_available = 1;
/* Append delimiter only if input is not empty and either
* the variable or the suffix are not empty
*/
if (smp->data.u.str.data && ((var_available && tmp.data.u.str.data) ||
arg_p[2].data.str.data)) {
max = arg_p[0].data.str.data;
if (max > tmpbuf->size - 1 - tmpbuf->data)
max = tmpbuf->size - 1 - tmpbuf->data;
if (max) {
memcpy(tmpbuf->area + tmpbuf->data, arg_p[0].data.str.area, max);
tmpbuf->data += max;
tmpbuf->area[tmpbuf->data] = 0;
}
}
/* Append variable contents if variable is found and turned into string. */
if (var_available) {
max = tmp.data.u.str.data;
if (max > tmpbuf->size - 1 - tmpbuf->data)
max = tmpbuf->size - 1 - tmpbuf->data;
if (max) {
memcpy(tmpbuf->area + tmpbuf->data, tmp.data.u.str.area, max);
tmpbuf->data += max;
tmpbuf->area[tmpbuf->data] = 0;
}
}
/* Append optional suffix. */
max = arg_p[2].data.str.data;
if (max > tmpbuf->size - 1 - tmpbuf->data)
max = tmpbuf->size - 1 - tmpbuf->data;
if (max) {
memcpy(tmpbuf->area + tmpbuf->data, arg_p[2].data.str.area, max);
tmpbuf->data += max;
tmpbuf->area[tmpbuf->data] = 0;
}
smp->data.u.str = *tmpbuf;
smp->data.type = SMP_T_STR;
smp_dup(smp);
free_trash_chunk(tmpbuf);
return 1;
}
/* Check the "add_item" converter's arguments and extracts the
* variable name and its scope.
*/
static int smp_check_add_item(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
/* Try to decode a variable. */
if (args[1].data.str.data > 0 && !vars_check_arg(&args[1], NULL)) {
memprintf(err, "failed to register variable name '%s'",
args[1].data.str.area);
return 0;
}
if (args[1].data.str.data == 0 && args[2].data.str.data == 0) {
memprintf(err, "one of the optional arguments has to be nonempty");
return 0;
}
return 1;
}
/* Compares string with a variable containing a string. Return value
* is compatible with strcmp(3)'s return value.
*/
static int sample_conv_strcmp(const struct arg *arg_p, struct sample *smp, void *private)
{
struct sample tmp;
int max, result;
smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
if (arg_p[0].type != ARGT_VAR)
return 0;
if (!sample_conv_var2smp(&arg_p[0].data.var, &tmp, SMP_T_STR))
return 0;
max = MIN(smp->data.u.str.data, tmp.data.u.str.data);
result = strncmp(smp->data.u.str.area, tmp.data.u.str.area, max);
if (result == 0) {
if (smp->data.u.str.data != tmp.data.u.str.data) {
if (smp->data.u.str.data < tmp.data.u.str.data) {
result = -1;
}
else {
result = 1;
}
}
}
smp->data.u.sint = result;
smp->data.type = SMP_T_SINT;
return 1;
}
/*
* This converter can takes a Host header value as defined by rfc9110#section-7.2
* Host = uri-host [ ":" port ] ;
* It returns the uri-host value in lowecase with the port stripped.
*/
static int sample_conv_host_only(const struct arg *arg_p, struct sample *smp, void *private)
{
/* Working cases: hostname00, hostname00:80, 127.0.0.1, 127.0.0.1:80, [::1], [::1]:80 */
char *beg = smp->data.u.str.area;
char *end = smp->data.u.str.area + smp->data.u.str.data - 1;
char *p;
for (p = end; p >= beg; p--) {
if (*p == ':' || *p == ']')
break;
}
if (p >= beg && *p == ':')
smp->data.u.str.data = p - beg;
/* if no port part was found, the hostname is the whole string */
smp->data.type = SMP_T_STR;
return sample_conv_str2lower(arg_p, smp, NULL);
}
/*
* This converter can takes a Host header value as defined by rfc9110#section-7.2
* Host = uri-host [ ":" port ] ;
* It returns the port value as a int.
*/
static int sample_conv_port_only(const struct arg *arg_p, struct sample *smp, void *private)
{
/* Working cases: hostname00, hostname00:80, 127.0.0.1, 127.0.0.1:80, [::1], [::1]:80 */
char *beg = smp->data.u.str.area;
char *end = smp->data.u.str.area + smp->data.u.str.data - 1;
char *p;
for (p = end; p >= beg; p--) {
if (*p == ':' || *p == ']')
break;
}
smp->data.type = SMP_T_SINT;
if (p >= beg && *p == ':' && ++p <= end) {
smp->data.u.sint = strl2ui(p, smp->data.u.str.data + smp->data.u.str.area - p);
} else {
smp->data.u.sint = 0;
}
return 1;
}
/* Takes a boolean as input. Returns the first argument if that boolean is true and
* the second argument otherwise.
*/
static int sample_conv_iif(const struct arg *arg_p, struct sample *smp, void *private)
{
smp->data.type = SMP_T_STR;
smp->flags |= SMP_F_CONST;
if (smp->data.u.sint) {
smp->data.u.str.data = arg_p[0].data.str.data;
smp->data.u.str.area = arg_p[0].data.str.area;
}
else {
smp->data.u.str.data = arg_p[1].data.str.data;
smp->data.u.str.area = arg_p[1].data.str.area;
}
return 1;
}
enum {
WHEN_COND_STOPPING,
WHEN_COND_NORMAL,
WHEN_COND_ERROR,
WHEN_COND_FORWARDED,
WHEN_COND_TOAPPLET,
WHEN_COND_PROCESSED,
WHEN_COND_ACL,
WHEN_COND_CONDITIONS
};
const char *when_cond_kw[WHEN_COND_CONDITIONS] = {
[WHEN_COND_STOPPING] = "stopping",
[WHEN_COND_NORMAL] = "normal",
[WHEN_COND_ERROR] = "error",
[WHEN_COND_FORWARDED] = "forwarded",
[WHEN_COND_TOAPPLET] = "toapplet",
[WHEN_COND_PROCESSED] = "processed",
[WHEN_COND_ACL] = "acl",
};
/* Evaluates a condition and decides whether or not to pass the input sample
* to the output. The purpose is to hide some info when certain conditions are
* (not) met. These conditions belong to a fixed list that can verify internal
* states (debug mode, too high load, reloading, server down, stream in error
* etc). The condition's sign is placed in arg_p[0].data.int. 0=direct, 1=inv.
* The condition keyword is in arg_p[1].data.int (WHEN_COND_*).
*/
static int sample_conv_when(const struct arg *arg_p, struct sample *smp, void *private)
{
struct session *sess = smp->sess;
struct stream *strm = smp->strm;
int neg = arg_p[0].data.sint;
int cond = arg_p[1].data.sint;
struct acl_sample *acl_sample;
int ret = 0;
switch (cond) {
case WHEN_COND_STOPPING:
ret = !!stopping;
break;
case WHEN_COND_NORMAL:
neg = !neg;
__fallthrough;
case WHEN_COND_ERROR:
if (strm &&
((strm->flags & SF_REDISP) ||
((strm->flags & SF_ERR_MASK) > SF_ERR_LOCAL) ||
(((strm->flags & SF_ERR_MASK) == SF_ERR_NONE) && strm->conn_retries) ||
((sess->fe->mode == PR_MODE_HTTP) && strm->txn && strm->txn->status >= 500)))
ret = 1;
break;
case WHEN_COND_FORWARDED: // true if forwarded to a connection
ret = !!sc_conn(smp->strm->scb);
break;
case WHEN_COND_TOAPPLET: // true if handled as an applet
ret = !!sc_appctx(smp->strm->scb);
break;
case WHEN_COND_PROCESSED: // true if forwarded or appctx
ret = sc_conn(smp->strm->scb) || sc_appctx(smp->strm->scb);
break;
case WHEN_COND_ACL: // true if the ACL pointed to by args[2] evaluates to true
acl_sample = arg_p[2].data.ptr;
ret = acl_exec_cond(&acl_sample->cond, smp->px, smp->sess, smp->strm, smp->opt) == ACL_TEST_PASS;
break;
}
ret = !!ret ^ !!neg;
if (!ret) {
/* kill the sample */
return 0;
}
/* pass the sample as-is */
return 1;
}
/* checks and resolves the type of the argument passed to when().
* It supports an optional '!' to negate the condition, followed by
* a keyword among the list above. Note that we're purposely declaring
* one extra arg because the first one will be split into two.
*/
static int check_when_cond(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
struct acl_sample *acl_sample;
const char *kw;
int neg = 0;
int i;
kw = args[0].data.str.area;
if (*kw == '!') {
kw++;
neg = 1;
}
for (i = 0; i < WHEN_COND_CONDITIONS; i++) {
if (strcmp(kw, when_cond_kw[i]) == 0)
break;
}
if (i == WHEN_COND_CONDITIONS) {
memprintf(err, "expects a supported keyword among {");
for (i = 0; i < WHEN_COND_CONDITIONS; i++)
memprintf(err, "%s%s%s", *err, when_cond_kw[i], (i == WHEN_COND_CONDITIONS - 1) ? "}" : ",");
memprintf(err, "%s but got '%s'", *err, kw);
return 0;
}
if (i == WHEN_COND_ACL) {
if (args[1].type != ARGT_STR || !*args[1].data.str.area) {
memprintf(err, "'acl' selector requires an extra argument with the ACL name");
return 0;
}
if (!curproxy) {
memprintf(err, "'acl' selector may only be used in the context of a proxy");
return 0;
}
acl_sample = calloc(1, sizeof(struct acl_sample) + sizeof(struct acl_term));
if (!acl_sample) {
memprintf(err, "not enough memory for 'acl' selector");
return 0;
}
LIST_INIT(&acl_sample->suite.terms);
LIST_INIT(&acl_sample->cond.suites);
LIST_APPEND(&acl_sample->cond.suites, &acl_sample->suite.list);
LIST_APPEND(&acl_sample->suite.terms, &acl_sample->terms[0].list);
acl_sample->cond.val = ~0U; // the keyword is valid everywhere for now.
/* build one term based on the ACL kw */
if (!(acl_sample->terms[0].acl = find_acl_by_name(args[1].data.str.area, &curproxy->acl)) &&
!(acl_sample->terms[0].acl = find_acl_default(args[1].data.str.area, &curproxy->acl, err, NULL, NULL, 0))) {
memprintf(err, "ACL '%s' not found", args[1].data.str.area);
return 0;
}
acl_sample->cond.use |= acl_sample->terms[0].acl->use;
acl_sample->cond.val &= acl_sample->terms[0].acl->val;
args[2].type = ARGT_PTR;
args[2].unresolved = 0;
args[2].resolve_ptr = NULL;
args[2].data.ptr = acl_sample;
}
chunk_destroy(&args[0].data.str);
if (args[1].type == ARGT_STR)
chunk_destroy(&args[1].data.str);
if (args[2].type == ARGT_STR)
chunk_destroy(&args[2].data.str);
// store condition
args[0].type = ARGT_SINT;
args[0].data.sint = neg; // '!' present
// and keyword
args[1].type = ARGT_SINT;
args[1].data.sint = i;
return 1;
}
#define GRPC_MSG_COMPRESS_FLAG_SZ 1 /* 1 byte */
#define GRPC_MSG_LENGTH_SZ 4 /* 4 bytes */
#define GRPC_MSG_HEADER_SZ (GRPC_MSG_COMPRESS_FLAG_SZ + GRPC_MSG_LENGTH_SZ)
/*
* Extract the field value of an input binary sample. Takes a mandatory argument:
* the protocol buffers field identifier (dotted notation) internally represented
* as an array of unsigned integers and its size.
* Return 1 if the field was found, 0 if not.
*/
static int sample_conv_ungrpc(const struct arg *arg_p, struct sample *smp, void *private)
{
unsigned char *pos;
size_t grpc_left;
pos = (unsigned char *)smp->data.u.str.area;
grpc_left = smp->data.u.str.data;
while (grpc_left > GRPC_MSG_HEADER_SZ) {
size_t grpc_msg_len, left;
grpc_msg_len = left = ntohl(read_u32(pos + GRPC_MSG_COMPRESS_FLAG_SZ));
pos += GRPC_MSG_HEADER_SZ;
grpc_left -= GRPC_MSG_HEADER_SZ;
if (grpc_left < left)
return 0;
if (protobuf_field_lookup(arg_p, smp, &pos, &left))
return 1;
grpc_left -= grpc_msg_len;
}
return 0;
}
static int sample_conv_protobuf(const struct arg *arg_p, struct sample *smp, void *private)
{
unsigned char *pos;
size_t left;
pos = (unsigned char *)smp->data.u.str.area;
left = smp->data.u.str.data;
return protobuf_field_lookup(arg_p, smp, &pos, &left);
}
static int sample_conv_protobuf_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (!args[1].type) {
args[1].type = ARGT_SINT;
args[1].data.sint = PBUF_T_BINARY;
}
else {
int pbuf_type;
pbuf_type = protobuf_type(args[1].data.str.area);
if (pbuf_type == -1) {
memprintf(err, "Wrong protocol buffer type '%s'", args[1].data.str.area);
return 0;
}
chunk_destroy(&args[1].data.str);
args[1].type = ARGT_SINT;
args[1].data.sint = pbuf_type;
}
return 1;
}
/*
* Extract the tag value of an input binary sample. Takes a mandatory argument:
* the FIX protocol tag identifier.
* Return 1 if the tag was found, 0 if not.
*/
static int sample_conv_fix_tag_value(const struct arg *arg_p, struct sample *smp, void *private)
{
struct ist value;
smp->flags &= ~SMP_F_MAY_CHANGE;
value = fix_tag_value(ist2(smp->data.u.str.area, smp->data.u.str.data),
arg_p[0].data.sint);
if (!istlen(value)) {
if (isttest(value)) {
/* value != IST_NULL, need more data */
smp->flags |= SMP_F_MAY_CHANGE;
}
return 0;
}
smp->data.u.str = ist2buf(value);
smp->flags |= SMP_F_CONST;
return 1;
}
/* This function checks the "fix_tag_value" converter configuration.
* It expects a "known" (by HAProxy) tag name or ID.
* Tag string names are converted to their ID counterpart because this is the
* format they are sent over the wire.
*/
static int sample_conv_fix_value_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
struct ist str;
unsigned int tag;
str = ist2(args[0].data.str.area, args[0].data.str.data);
tag = fix_tagid(str);
if (!tag) {
memprintf(err, "Unknown FIX tag name '%s'", args[0].data.str.area);
return 0;
}
chunk_destroy(&args[0].data.str);
args[0].type = ARGT_SINT;
args[0].data.sint = tag;
return 1;
}
/*
* Checks that a buffer contains a valid FIX message
*
* Return 1 if the check could be run, 0 if not.
* The result of the analyse itself is stored in <smp> as a boolean
*/
static int sample_conv_fix_is_valid(const struct arg *arg_p, struct sample *smp, void *private)
{
struct ist msg;
msg = ist2(smp->data.u.str.area, smp->data.u.str.data);
smp->flags &= ~SMP_F_MAY_CHANGE;
switch (fix_validate_message(msg)) {
case FIX_VALID_MESSAGE:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 1;
return 1;
case FIX_NEED_MORE_DATA:
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
case FIX_INVALID_MESSAGE:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 0;
return 1;
}
return 0;
}
/*
* Extract the field value of an input binary sample containing an MQTT packet.
* Takes 2 mandatory arguments:
* - packet type
* - field name
*
* return 1 if the field was found, 0 if not.
*/
static int sample_conv_mqtt_field_value(const struct arg *arg_p, struct sample *smp, void *private)
{
struct ist pkt, value;
int type, fieldname_id;
pkt = ist2(smp->data.u.str.area, smp->data.u.str.data);
type = arg_p[0].data.sint;
fieldname_id = arg_p[1].data.sint;
smp->flags &= ~SMP_F_MAY_CHANGE;
value = mqtt_field_value(pkt, type, fieldname_id);
if (!istlen(value)) {
if (isttest(value)) {
/* value != IST_NULL, need more data */
smp->flags |= SMP_F_MAY_CHANGE;
}
return 0;
}
smp->data.u.str = ist2buf(value);
smp->flags |= SMP_F_CONST;
return 1;
}
/*
* this function checks the "mqtt_field_value" converter configuration.
* It expects a known packet type name or ID and a field name, in this order
*
* Args[0] will be turned into a MQTT_CPT_* value for direct matching when parsing
* a packet.
*/
static int sample_conv_mqtt_field_value_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
int type, fieldname_id;
/* check the MQTT packet type is valid */
type = mqtt_typeid(ist2(args[0].data.str.area, args[0].data.str.data));
if (type == MQTT_CPT_INVALID) {
memprintf(err, "Unknown MQTT type '%s'", args[0].data.str.area);
return 0;
}
/* check the field name belongs to the MQTT packet type */
fieldname_id = mqtt_check_type_fieldname(type, ist2(args[1].data.str.area, args[1].data.str.data));
if (fieldname_id == MQTT_FN_INVALID) {
memprintf(err, "Unknown MQTT field name '%s' for packet type '%s'", args[1].data.str.area,
args[0].data.str.area);
return 0;
}
/* save numeric counterparts of type and field name */
chunk_destroy(&args[0].data.str);
chunk_destroy(&args[1].data.str);
args[0].type = ARGT_SINT;
args[0].data.sint = type;
args[1].type = ARGT_SINT;
args[1].data.sint = fieldname_id;
return 1;
}
/*
* Checks that <smp> contains a valid MQTT message
*
* The function returns 1 if the check was run to its end, 0 otherwise.
* The result of the analyse itself is stored in <smp> as a boolean.
*/
static int sample_conv_mqtt_is_valid(const struct arg *arg_p, struct sample *smp, void *private)
{
struct ist msg;
msg = ist2(smp->data.u.str.area, smp->data.u.str.data);
smp->flags &= ~SMP_F_MAY_CHANGE;
switch (mqtt_validate_message(msg, NULL)) {
case FIX_VALID_MESSAGE:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 1;
return 1;
case FIX_NEED_MORE_DATA:
smp->flags |= SMP_F_MAY_CHANGE;
return 0;
case FIX_INVALID_MESSAGE:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 0;
return 1;
}
return 0;
}
/* This function checks the "strcmp" converter's arguments and extracts the
* variable name and its scope.
*/
static int smp_check_strcmp(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (!args[0].data.str.data) {
memprintf(err, "missing variable name");
return 0;
}
/* Try to decode a variable. */
if (vars_check_arg(&args[0], NULL))
return 1;
memprintf(err, "failed to register variable name '%s'",
args[0].data.str.area);
return 0;
}
/**/
static int sample_conv_htonl(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *tmp;
uint32_t n;
n = htonl((uint32_t)smp->data.u.sint);
tmp = get_trash_chunk();
memcpy(b_head(tmp), &n, 4);
b_add(tmp, 4);
smp->data.u.str = *tmp;
smp->data.type = SMP_T_BIN;
return 1;
}
/**/
static int sample_conv_cut_crlf(const struct arg *arg_p, struct sample *smp, void *private)
{
char *p;
size_t l;
p = smp->data.u.str.area;
for (l = 0; l < smp->data.u.str.data; l++) {
if (*(p+l) == '\r' || *(p+l) == '\n')
break;
}
smp->data.u.str.data = l;
return 1;
}
/**/
static int sample_conv_ltrim(const struct arg *arg_p, struct sample *smp, void *private)
{
char *delimiters, *p;
size_t dlen, l;
delimiters = arg_p[0].data.str.area;
dlen = arg_p[0].data.str.data;
l = smp->data.u.str.data;
p = smp->data.u.str.area;
while (l && memchr(delimiters, *p, dlen) != NULL) {
p++;
l--;
}
smp->data.u.str.area = p;
smp->data.u.str.data = l;
return 1;
}
/**/
static int sample_conv_rtrim(const struct arg *arg_p, struct sample *smp, void *private)
{
char *delimiters, *p;
size_t dlen, l;
delimiters = arg_p[0].data.str.area;
dlen = arg_p[0].data.str.data;
l = smp->data.u.str.data;
p = smp->data.u.str.area + l - 1;
while (l && memchr(delimiters, *p, dlen) != NULL) {
p--;
l--;
}
smp->data.u.str.data = l;
return 1;
}
/* This function checks the "json_query" converter's arguments. */
static int sample_check_json_query(struct arg *arg, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (arg[0].data.str.data == 0) {
memprintf(err, "json_path must not be empty");
return 0;
}
if (arg[1].data.str.data != 0) {
if (strcmp(arg[1].data.str.area, "int") != 0) {
memprintf(err, "output_type only supports \"int\" as argument");
return 0;
} else {
arg[1].type = ARGT_SINT;
arg[1].data.sint = 0;
}
}
return 1;
}
/* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per
* the recommendation for interoperable integers in section 6 of RFC 7159.
*/
#define JSON_INT_MAX ((1LL << 53) - 1)
#define JSON_INT_MIN (-JSON_INT_MAX)
/* This sample function get the value from a given json string.
* The mjson library is used to parse the JSON struct
*/
static int sample_conv_json_query(const struct arg *args, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
const char *token; /* holds the temporary string from mjson_find */
int token_size; /* holds the length of <token> */
enum mjson_tok token_type;
token_type = mjson_find(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, &token, &token_size);
switch (token_type) {
case MJSON_TOK_NUMBER:
if (args[1].type == ARGT_SINT) {
smp->data.u.sint = strtoll(token, NULL, 0);
if (smp->data.u.sint < JSON_INT_MIN || smp->data.u.sint > JSON_INT_MAX)
return 0;
smp->data.type = SMP_T_SINT;
return 1;
} else {
double double_val;
if (mjson_get_number(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, &double_val) == 0)
return 0;
trash->data = snprintf(trash->area,trash->size,"%g",double_val);
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
return 1;
}
case MJSON_TOK_TRUE:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 1;
return 1;
case MJSON_TOK_FALSE:
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 0;
return 1;
case MJSON_TOK_STRING: {
int len;
len = mjson_get_string(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, trash->area, trash->size);
if (len == -1) {
/* invalid string */
return 0;
}
trash->data = len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
return 1;
}
case MJSON_TOK_ARRAY: {
// We copy the complete array, including square brackets into the return buffer
// result looks like: ["manage-account","manage-account-links","view-profile"]
trash->data = b_putblk(trash, token, token_size);
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
return 1;
}
case MJSON_TOK_NULL:
case MJSON_TOK_OBJECT:
/* We cannot handle these. */
return 0;
case MJSON_TOK_INVALID:
/* Nothing matches the query. */
return 0;
case MJSON_TOK_KEY:
/* This is not a valid return value according to the
* mjson documentation, but we handle it to benefit
* from '-Wswitch'.
*/
return 0;
}
my_unreachable();
return 0;
}
#ifdef USE_OPENSSL
static int sample_conv_jwt_verify_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
enum jwt_alg alg = JWT_ALG_DEFAULT;
MAJOR: jwt: Allow certificate instead of public key in jwt_verify converter The 'jwt_verify' converter could only be passed public keys as second parameter instead of full-on public certificates. This patch allows proper certificates to be used. Those certificates can be loaded in ckch_stores like any other certificate which means that all the certificate-related operations that can be made via the CLI can now benefit JWT validation as well. We now have two ways JWT validation can work, the legacy one which only relies on public keys which could not be stored in ckch_stores without some in depth changes in the way the ckch_stores are built. In this legacy way, the public keys are fully stored in a cache dedicated to JWT only which does not have any CLI commands and any way to update them during runtime. It also requires that all the public keys used are passed at least once explicitely to the 'jwt_verify' converter so that they can be loaded during init. The new way uses actual certificates, either already stored in the ckch_store tree (if predefined in a crt-store or already used previously in the configuration) or loaded in the ckch_store tree during init if they are explicitely used in the configuration like so: var(txn.bearer),jwt_verify(txn.jwt_alg,"cert.pem") When using a variable (or any other way that can only be resolved during runtime) in place of the converter's <key> parameter, the first time we encounter a new value (for which we don't have any entry in the jwt tree) we will lock the ckch_store tree and try to perform a lookup in it. If the lookup fails, an entry will still be inserted into the jwt tree so that any following call with this value avoids performing the ckch_store tree lookup.
2025-06-30 10:56:26 -04:00
int retval = 0;
vars_check_arg(&args[0], NULL);
vars_check_arg(&args[1], NULL);
if (args[0].type == ARGT_STR) {
alg = jwt_parse_alg(args[0].data.str.area, args[0].data.str.data);
if (alg == JWT_ALG_DEFAULT) {
memprintf(err, "unknown JWT algorithm: %s", args[0].data.str.area);
return 0;
}
}
if (args[1].type == ARGT_STR) {
switch (alg) {
JWS_ALG_HS256:
JWS_ALG_HS384:
JWS_ALG_HS512:
/* don't try to load a file with HMAC algorithms */
MAJOR: jwt: Allow certificate instead of public key in jwt_verify converter The 'jwt_verify' converter could only be passed public keys as second parameter instead of full-on public certificates. This patch allows proper certificates to be used. Those certificates can be loaded in ckch_stores like any other certificate which means that all the certificate-related operations that can be made via the CLI can now benefit JWT validation as well. We now have two ways JWT validation can work, the legacy one which only relies on public keys which could not be stored in ckch_stores without some in depth changes in the way the ckch_stores are built. In this legacy way, the public keys are fully stored in a cache dedicated to JWT only which does not have any CLI commands and any way to update them during runtime. It also requires that all the public keys used are passed at least once explicitely to the 'jwt_verify' converter so that they can be loaded during init. The new way uses actual certificates, either already stored in the ckch_store tree (if predefined in a crt-store or already used previously in the configuration) or loaded in the ckch_store tree during init if they are explicitely used in the configuration like so: var(txn.bearer),jwt_verify(txn.jwt_alg,"cert.pem") When using a variable (or any other way that can only be resolved during runtime) in place of the converter's <key> parameter, the first time we encounter a new value (for which we don't have any entry in the jwt tree) we will lock the ckch_store tree and try to perform a lookup in it. If the lookup fails, an entry will still be inserted into the jwt tree so that any following call with this value avoids performing the ckch_store tree lookup.
2025-06-30 10:56:26 -04:00
retval = 1;
break;
default:
MAJOR: jwt: Allow certificate instead of public key in jwt_verify converter The 'jwt_verify' converter could only be passed public keys as second parameter instead of full-on public certificates. This patch allows proper certificates to be used. Those certificates can be loaded in ckch_stores like any other certificate which means that all the certificate-related operations that can be made via the CLI can now benefit JWT validation as well. We now have two ways JWT validation can work, the legacy one which only relies on public keys which could not be stored in ckch_stores without some in depth changes in the way the ckch_stores are built. In this legacy way, the public keys are fully stored in a cache dedicated to JWT only which does not have any CLI commands and any way to update them during runtime. It also requires that all the public keys used are passed at least once explicitely to the 'jwt_verify' converter so that they can be loaded during init. The new way uses actual certificates, either already stored in the ckch_store tree (if predefined in a crt-store or already used previously in the configuration) or loaded in the ckch_store tree during init if they are explicitely used in the configuration like so: var(txn.bearer),jwt_verify(txn.jwt_alg,"cert.pem") When using a variable (or any other way that can only be resolved during runtime) in place of the converter's <key> parameter, the first time we encounter a new value (for which we don't have any entry in the jwt tree) we will lock the ckch_store tree and try to perform a lookup in it. If the lookup fails, an entry will still be inserted into the jwt tree so that any following call with this value avoids performing the ckch_store tree lookup.
2025-06-30 10:56:26 -04:00
retval = (jwt_tree_load_cert(args[1].data.str.area, args[1].data.str.data,
file, line, err) == 0);
break;
}
MAJOR: jwt: Allow certificate instead of public key in jwt_verify converter The 'jwt_verify' converter could only be passed public keys as second parameter instead of full-on public certificates. This patch allows proper certificates to be used. Those certificates can be loaded in ckch_stores like any other certificate which means that all the certificate-related operations that can be made via the CLI can now benefit JWT validation as well. We now have two ways JWT validation can work, the legacy one which only relies on public keys which could not be stored in ckch_stores without some in depth changes in the way the ckch_stores are built. In this legacy way, the public keys are fully stored in a cache dedicated to JWT only which does not have any CLI commands and any way to update them during runtime. It also requires that all the public keys used are passed at least once explicitely to the 'jwt_verify' converter so that they can be loaded during init. The new way uses actual certificates, either already stored in the ckch_store tree (if predefined in a crt-store or already used previously in the configuration) or loaded in the ckch_store tree during init if they are explicitely used in the configuration like so: var(txn.bearer),jwt_verify(txn.jwt_alg,"cert.pem") When using a variable (or any other way that can only be resolved during runtime) in place of the converter's <key> parameter, the first time we encounter a new value (for which we don't have any entry in the jwt tree) we will lock the ckch_store tree and try to perform a lookup in it. If the lookup fails, an entry will still be inserted into the jwt tree so that any following call with this value avoids performing the ckch_store tree lookup.
2025-06-30 10:56:26 -04:00
} else if (args[1].type == ARGT_VAR) {
/* We will try to resolve the var during runtime because the
* processing might work if it actually points to an already
* existing ckch_store.
*/
retval = 1;
}
MAJOR: jwt: Allow certificate instead of public key in jwt_verify converter The 'jwt_verify' converter could only be passed public keys as second parameter instead of full-on public certificates. This patch allows proper certificates to be used. Those certificates can be loaded in ckch_stores like any other certificate which means that all the certificate-related operations that can be made via the CLI can now benefit JWT validation as well. We now have two ways JWT validation can work, the legacy one which only relies on public keys which could not be stored in ckch_stores without some in depth changes in the way the ckch_stores are built. In this legacy way, the public keys are fully stored in a cache dedicated to JWT only which does not have any CLI commands and any way to update them during runtime. It also requires that all the public keys used are passed at least once explicitely to the 'jwt_verify' converter so that they can be loaded during init. The new way uses actual certificates, either already stored in the ckch_store tree (if predefined in a crt-store or already used previously in the configuration) or loaded in the ckch_store tree during init if they are explicitely used in the configuration like so: var(txn.bearer),jwt_verify(txn.jwt_alg,"cert.pem") When using a variable (or any other way that can only be resolved during runtime) in place of the converter's <key> parameter, the first time we encounter a new value (for which we don't have any entry in the jwt tree) we will lock the ckch_store tree and try to perform a lookup in it. If the lookup fails, an entry will still be inserted into the jwt tree so that any following call with this value avoids performing the ckch_store tree lookup.
2025-06-30 10:56:26 -04:00
return retval;
}
/* Check that a JWT's signature is correct */
static int sample_conv_jwt_verify(const struct arg *args, struct sample *smp, void *private)
{
struct sample alg_smp, key_smp;
enum jwt_vrfy_status ret;
struct buffer *input = NULL;
struct buffer *alg = NULL;
struct buffer *key = NULL;
int retval = 0;
/* The two following calls to 'sample_conv_var2smp_str' will both make
* use of the preallocated trash buffer (via get_trash_chunk call in
* smp_dup) which would end up erasing the contents of the 'smp' input
* buffer.
*/
input = alloc_trash_chunk();
if (!input)
return 0;
alg = alloc_trash_chunk();
if (!alg)
goto end;
key = alloc_trash_chunk();
if (!key)
goto end;
if (!chunk_cpy(input, &smp->data.u.str))
goto end;
smp_set_owner(&alg_smp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_str(&args[0], &alg_smp))
goto end;
if (chunk_printf(alg, "%.*s", (int)b_data(&alg_smp.data.u.str), b_orig(&alg_smp.data.u.str)) <= 0)
goto end;
smp_set_owner(&key_smp, smp->px, smp->sess, smp->strm, smp->opt);
if (!sample_conv_var2smp_str(&args[1], &key_smp))
goto end;
if (chunk_printf(key, "%.*s", (int)b_data(&key_smp.data.u.str), b_orig(&key_smp.data.u.str)) <= 0)
goto end;
ret = jwt_verify(input, alg, key);
smp->data.type = SMP_T_SINT;
smp->data.u.sint = ret;
retval = 1;
end:
free_trash_chunk(input);
free_trash_chunk(alg);
free_trash_chunk(key);
return retval;
}
/*
* Returns the decoded header or payload of a JWT if no parameter is given, or
* the value of the specified field of the corresponding JWT subpart if a
* parameter is given.
*/
static int sample_conv_jwt_member_query(const struct arg *args, struct sample *smp,
void *private, enum jwt_elt member)
{
struct jwt_item items[JWT_ELT_MAX] = { { 0 } };
unsigned int item_num = member + 1; /* We don't need to tokenize the full token */
struct buffer *decoded_header = get_trash_chunk();
int retval = 0;
int ret;
jwt_tokenize(&smp->data.u.str, items, &item_num);
if (item_num < member + 1)
goto end;
ret = base64urldec(items[member].start, items[member].length,
decoded_header->area, decoded_header->size);
if (ret == -1)
goto end;
decoded_header->data = ret;
if (args[0].type != ARGT_STR) {
smp->data.u.str = *decoded_header;
smp->data.type = SMP_T_STR;
goto end;
}
/* We look for a specific field of the header or payload part of the JWT */
smp->data.u.str = *decoded_header;
retval = sample_conv_json_query(args, smp, private);
end:
return retval;
}
/* This function checks the "jwt_header_query" and "jwt_payload_query" converters' arguments.
* It is based on the "json_query" converter's check with the only difference
* being that the jwt converters can take 0 parameters as well.
*/
static int sample_conv_jwt_query_check(struct arg *arg, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (arg[1].data.str.data != 0) {
if (strcmp(arg[1].data.str.area, "int") != 0) {
memprintf(err, "output_type only supports \"int\" as argument");
return 0;
} else {
arg[1].type = ARGT_SINT;
arg[1].data.sint = 0;
}
}
return 1;
}
/*
* If no parameter is given, return the decoded header part of a JWT (the first
* base64 encoded part, corresponding to the JOSE header).
* If a parameter is given, this converter acts as a "json_query" on this
* decoded JSON.
*/
static int sample_conv_jwt_header_query(const struct arg *args, struct sample *smp, void *private)
{
return sample_conv_jwt_member_query(args, smp, private, JWT_ELT_JOSE);
}
/*
* If no parameter is given, return the decoded payload part of a JWT (the
* second base64 encoded part, which contains all the claims). If a parameter
* is given, this converter acts as a "json_query" on this decoded JSON.
*/
static int sample_conv_jwt_payload_query(const struct arg *args, struct sample *smp, void *private)
{
return sample_conv_jwt_member_query(args, smp, private, JWT_ELT_CLAIMS);
}
#endif /* USE_OPENSSL */
/************************************************************************/
/* All supported sample fetch functions must be declared here */
/************************************************************************/
/* returns the actconn */
static int
smp_fetch_actconn(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_SINT;
smp->data.u.sint = actconn;
return 1;
}
/* force TRUE to be returned at the fetch level */
static int
smp_fetch_true(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
if (!smp_make_rw(smp))
return 0;
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 1;
return 1;
}
/* force FALSE to be returned at the fetch level */
static int
smp_fetch_false(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 0;
return 1;
}
/* retrieve environment variable $1 as a string */
static int
smp_fetch_env(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
char *env;
if (args[0].type != ARGT_STR)
return 0;
env = getenv(args[0].data.str.area);
if (!env)
return 0;
smp->data.type = SMP_T_STR;
smp->flags = SMP_F_CONST;
smp->data.u.str.area = env;
smp->data.u.str.data = strlen(env);
return 1;
}
/* Validates the data unit argument passed to "date" fetch. Argument 1 support an
* optional string representing the unit of the result: "s" for seconds, "ms" for
* milliseconds and "us" for microseconds.
* Returns 0 on error and non-zero if OK.
*/
int smp_check_date_unit(struct arg *args, char **err)
{
if (args[1].type == ARGT_STR) {
long long int unit;
if (strcmp(args[1].data.str.area, "s") == 0) {
unit = TIME_UNIT_S;
}
else if (strcmp(args[1].data.str.area, "ms") == 0) {
unit = TIME_UNIT_MS;
}
else if (strcmp(args[1].data.str.area, "us") == 0) {
unit = TIME_UNIT_US;
}
else {
memprintf(err, "expects 's', 'ms' or 'us', got '%s'",
args[1].data.str.area);
return 0;
}
chunk_destroy(&args[1].data.str);
args[1].type = ARGT_SINT;
args[1].data.sint = unit;
}
else if (args[1].type != ARGT_STOP) {
memprintf(err, "Unexpected arg type");
return 0;
}
return 1;
}
/* retrieve the current local date in epoch time, converts it to milliseconds
* or microseconds if asked to in optional args[1] unit param, and applies an
* optional args[0] offset.
*/
static int
smp_fetch_date(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.u.sint = date.tv_sec;
/* report in milliseconds */
if (args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_MS) {
smp->data.u.sint *= 1000;
smp->data.u.sint += date.tv_usec / 1000;
}
/* report in microseconds */
else if (args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_US) {
smp->data.u.sint *= 1000000;
smp->data.u.sint += date.tv_usec;
}
/* add offset */
if (args[0].type == ARGT_SINT)
smp->data.u.sint += args[0].data.sint;
smp->data.type = SMP_T_SINT;
smp->flags |= SMP_F_VOL_TEST | SMP_F_MAY_CHANGE;
return 1;
}
/* retrieve the current microsecond part of the date */
static int
smp_fetch_date_us(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.u.sint = date.tv_usec;
smp->data.type = SMP_T_SINT;
smp->flags |= SMP_F_VOL_TEST | SMP_F_MAY_CHANGE;
return 1;
}
/* returns the hostname */
static int
smp_fetch_hostname(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_STR;
smp->flags = SMP_F_CONST;
smp->data.u.str.area = hostname;
smp->data.u.str.data = strlen(hostname);
return 1;
}
/* returns the number of processes */
static int
smp_fetch_nbproc(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_SINT;
smp->data.u.sint = 1;
return 1;
}
/* returns the PID of the current process */
static int
smp_fetch_pid(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_SINT;
smp->data.u.sint = pid;
return 1;
}
/* returns the number of the current process (between 1 and nbproc */
static int
smp_fetch_proc(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_SINT;
smp->data.u.sint = 1;
return 1;
}
/* returns the number of the current thread (between 1 and nbthread */
static int
smp_fetch_thread(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_SINT;
smp->data.u.sint = tid;
return 1;
}
/* generate a random 32-bit integer for whatever purpose, with an optional
* range specified in argument.
*/
static int
smp_fetch_rand(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.u.sint = statistical_prng();
/* reduce if needed. Don't do a modulo, use all bits! */
if (args[0].type == ARGT_SINT)
smp->data.u.sint = ((u64)smp->data.u.sint * (u64)args[0].data.sint) >> 32;
smp->data.type = SMP_T_SINT;
smp->flags |= SMP_F_VOL_TEST | SMP_F_MAY_CHANGE;
return 1;
}
/* returns true if the current process is stopping */
static int
smp_fetch_stopping(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = stopping;
return 1;
}
/* returns the number of calls of the current stream's process_stream() */
static int
smp_fetch_cpu_calls(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->data.u.sint = smp->strm->task->calls;
return 1;
}
/* returns the average number of nanoseconds spent processing the stream per call */
static int
smp_fetch_cpu_ns_avg(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->data.u.sint = smp->strm->task->calls ? smp->strm->cpu_time / smp->strm->task->calls : 0;
return 1;
}
/* returns the total number of nanoseconds spent processing the stream */
static int
smp_fetch_cpu_ns_tot(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->data.u.sint = smp->strm->cpu_time;
return 1;
}
/* returns the average number of nanoseconds per call spent waiting for other tasks to be processed */
static int
smp_fetch_lat_ns_avg(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->data.u.sint = smp->strm->task->calls ? smp->strm->lat_time / smp->strm->task->calls : 0;
return 1;
}
/* returns the total number of nanoseconds per call spent waiting for other tasks to be processed */
static int
smp_fetch_lat_ns_tot(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->data.u.sint = smp->strm->lat_time;
return 1;
}
static int smp_fetch_const_str(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->flags |= SMP_F_CONST;
smp->data.type = SMP_T_STR;
smp->data.u.str.area = args[0].data.str.area;
smp->data.u.str.data = args[0].data.str.data;
return 1;
}
static int smp_check_const_bool(struct arg *args, char **err)
{
if (strcasecmp(args[0].data.str.area, "true") == 0 ||
strcasecmp(args[0].data.str.area, "1") == 0) {
chunk_destroy(&args[0].data.str);
args[0].type = ARGT_SINT;
args[0].data.sint = 1;
return 1;
}
if (strcasecmp(args[0].data.str.area, "false") == 0 ||
strcasecmp(args[0].data.str.area, "0") == 0) {
chunk_destroy(&args[0].data.str);
args[0].type = ARGT_SINT;
args[0].data.sint = 0;
return 1;
}
memprintf(err, "Expects 'true', 'false', '0' or '1'");
return 0;
}
static int smp_fetch_const_bool(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = args[0].data.sint;
return 1;
}
static int smp_fetch_const_int(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_SINT;
smp->data.u.sint = args[0].data.sint;
return 1;
}
static int smp_fetch_const_ipv4(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_IPV4;
smp->data.u.ipv4 = args[0].data.ipv4;
return 1;
}
static int smp_fetch_const_ipv6(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_IPV6;
smp->data.u.ipv6 = args[0].data.ipv6;
return 1;
}
static int smp_check_const_bin(struct arg *args, char **err)
{
char *binstr = NULL;
int binstrlen;
if (!parse_binary(args[0].data.str.area, &binstr, &binstrlen, err))
return 0;
chunk_destroy(&args[0].data.str);
args[0].type = ARGT_STR;
args[0].data.str.area = binstr;
args[0].data.str.data = binstrlen;
return 1;
}
static int smp_fetch_const_bin(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->flags |= SMP_F_CONST;
smp->data.type = SMP_T_BIN;
smp->data.u.str.area = args[0].data.str.area;
smp->data.u.str.data = args[0].data.str.data;
return 1;
}
static int smp_check_const_meth(struct arg *args, char **err)
{
enum http_meth_t meth;
int i;
meth = find_http_meth(args[0].data.str.area, args[0].data.str.data);
if (meth != HTTP_METH_OTHER) {
chunk_destroy(&args[0].data.str);
args[0].type = ARGT_SINT;
args[0].data.sint = meth;
} else {
/* Check method availability. A method is a token defined as :
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
* "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
* token = 1*tchar
*/
for (i = 0; i < args[0].data.str.data; i++) {
if (!HTTP_IS_TOKEN(args[0].data.str.area[i])) {
memprintf(err, "expects valid method.");
return 0;
}
}
}
return 1;
}
static int smp_fetch_const_meth(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_METH;
if (args[0].type == ARGT_SINT) {
smp->flags &= ~SMP_F_CONST;
smp->data.u.meth.meth = args[0].data.sint;
smp->data.u.meth.str.area = "";
smp->data.u.meth.str.data = 0;
} else {
smp->flags |= SMP_F_CONST;
smp->data.u.meth.meth = HTTP_METH_OTHER;
smp->data.u.meth.str.area = args[0].data.str.area;
smp->data.u.meth.str.data = args[0].data.str.data;
}
return 1;
}
// This function checks the "uuid" sample's arguments.
// Function won't get called when no parameter is specified (maybe a bug?)
static int smp_check_uuid(struct arg *args, char **err)
{
if (!args[0].type) {
args[0].type = ARGT_SINT;
args[0].data.sint = 4;
} else {
switch (args[0].data.sint) {
case 4:
case 7:
break;
default:
memprintf(err, "Unsupported UUID version: '%lld'", args[0].data.sint);
return 0;
}
}
return 1;
}
// Generate a RFC 9562 UUID (default is v4 = fully random)
static int smp_fetch_uuid(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
long long int type = -1;
if (!args[0].type) {
type = 4;
} else {
type = args[0].data.sint;
}
switch (type) {
case 4:
ha_generate_uuid_v4(&trash);
break;
case 7:
ha_generate_uuid_v7(&trash);
break;
default:
return 0;
}
smp->data.type = SMP_T_STR;
smp->flags = SMP_F_VOL_TEST | SMP_F_MAY_CHANGE;
smp->data.u.str = trash;
return 1;
}
/* returns the uptime in seconds */
static int
smp_fetch_uptime(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_SINT;
smp->data.u.sint = ns_to_sec(now_ns - start_time_ns);
return 1;
}
/* Check if QUIC support was compiled and was not disabled by "no-quic" global option */
static int smp_fetch_quic_enabled(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
smp->data.type = SMP_T_BOOL;
smp->flags = 0;
#ifdef USE_QUIC
smp->data.u.sint = !(global.tune.options & GTUNE_NO_QUIC);
#else
smp->data.u.sint = 0;
#endif
return smp->data.u.sint;
}
/* Timing events re{q,s}.timer. */
static int smp_fetch_reX_timers(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct strm_logs *logs;
int t_request = -1;
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->flags = 0;
logs = &smp->strm->logs;
if ((llong)(logs->request_ts - logs->accept_ts) >= 0)
t_request = ns_to_ms(logs->request_ts - logs->accept_ts);
/* req.timer. */
if (kw[2] == 'q') {
switch (kw[10]) {
/* req.timer.idle (%Ti) */
case 'i':
smp->data.u.sint = logs->t_idle;
break;
/* req.timer.tq (%Tq) */
case 't':
smp->data.u.sint = t_request;
break;
/* req.timer.hdr (%TR) */
case 'h':
smp->data.u.sint = (t_request >= 0) ? t_request - logs->t_idle - logs->t_handshake : -1;
break;
/* req.timer.queue (%Tw) */
case 'q':
smp->data.u.sint = (logs->t_queue >= 0) ? logs->t_queue - t_request : -1;
break;
default:
goto error;
}
} else {
/* res.timer. */
switch (kw[10]) {
/* res.timer.hdr (%Tr) */
case 'h':
smp->data.u.sint = (logs->t_data >= 0) ? logs->t_data - logs->t_connect : -1;
break;
/* res.timer.data (%Td) */
case 'd':
smp->data.u.sint = (logs->t_data >= 0) ? logs->t_close - logs->t_data : -1;
break;
default:
goto error;
}
}
return 1;
error:
return 0;
}
/* Timing events txn. */
static int smp_fetch_txn_timers(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct strm_logs *logs;
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->flags = 0;
logs = &smp->strm->logs;
/* txn.timer. */
switch (kw[10]) {
/* txn.timer.total (%Ta) */
case 't':
smp->data.u.sint = logs->t_close - (logs->t_idle >= 0 ? logs->t_idle + logs->t_handshake : 0);
break;
/* txn.timer.user (%Tu) */
case 'u':
smp->data.u.sint = logs->t_close - (logs->t_idle >= 0 ? logs->t_idle : 0);
break;
default:
goto error;
}
return 1;
error:
return 0;
}
/* Server conn queueing infos - bc_{be,srv}_queue */
static int smp_fetch_conn_queues(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct strm_logs *logs;
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->flags = 0;
logs = &smp->strm->logs;
if (kw[3] == 'b') {
/* bc_be_queue */
smp->data.u.sint = logs->prx_queue_pos;
}
else {
/* bc_srv_queue */
smp->data.u.sint = logs->srv_queue_pos;
}
return 1;
}
/* Timing events {f,bc}.timer. */
static int smp_fetch_conn_timers(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct strm_logs *logs;
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->flags = 0;
logs = &smp->strm->logs;
if (kw[0] == 'b') {
/* fc.timer. */
switch (kw[9]) {
/* bc.timer.connect (%Tc) */
case 'c':
smp->data.u.sint = (logs->t_connect >= 0) ? logs->t_connect - logs->t_queue : -1;
break;
default:
goto error;
}
} else {
/* fc.timer. */
switch (kw[9]) {
/* fc.timer.handshake (%Th) */
case 'h':
smp->data.u.sint = logs->t_handshake;
break;
/* fc,timer.total (%Tt) */
case 't':
smp->data.u.sint = logs->t_close;
break;
default:
goto error;
}
}
return 1;
error:
return 0;
}
/* bytes_{in,out} */
static int smp_fetch_bytes(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
struct strm_logs *logs;
if (!smp->strm)
return 0;
smp->data.type = SMP_T_SINT;
smp->flags = 0;
logs = &smp->strm->logs;
if (!logs)
return 0;
if (kw[6] == 'i') { /* bytes_in */
smp->data.u.sint = logs->bytes_in;
} else { /* bytes_out */
smp->data.u.sint = logs->bytes_out;
}
return 1;
}
static int sample_conv_bytes_check(struct arg *args, struct sample_conv *conv,
const char *file, int line, char **err)
{
// arg0 is not optional, must be >= 0
if (!check_operator(&args[0], conv, file, line, err)) {
return 0;
}
if (args[0].type != ARGT_VAR) {
if (args[0].type != ARGT_SINT || args[0].data.sint < 0) {
memprintf(err, "expects a non-negative integer");
return 0;
}
}
// arg1 is optional, must be > 0
if (args[1].type != ARGT_STOP) {
if (!check_operator(&args[1], conv, file, line, err)) {
return 0;
}
if (args[1].type != ARGT_VAR) {
if (args[1].type != ARGT_SINT || args[1].data.sint <= 0) {
memprintf(err, "expects a positive integer");
return 0;
}
}
}
return 1;
}
static struct sample_fetch_kw_list smp_logs_kws = {ILH, {
{ "bytes_in", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "bytes_out", smp_fetch_bytes, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "txn.timer.total", smp_fetch_txn_timers, 0, NULL, SMP_T_SINT, SMP_USE_TXFIN }, /* "Ta" */
{ "txn.timer.user", smp_fetch_txn_timers, 0, NULL, SMP_T_SINT, SMP_USE_TXFIN }, /* "Tu" */
{ "bc.timer.connect", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "Tc" */
{ "bc_be_queue", smp_fetch_conn_queues, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "bq" */
{ "bc_srv_queue", smp_fetch_conn_queues, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "sq" */
{ "fc.timer.handshake", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI }, /* "Th" */
{ "fc.timer.total", smp_fetch_conn_timers, 0, NULL, SMP_T_SINT, SMP_USE_SSFIN }, /* "Tt" */
{ "req.timer.idle", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Ti" */
{ "req.timer.tq", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "Tq" */
{ "req.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV }, /* "TR" */
{ "req.timer.queue", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_L4SRV }, /* "Tw" */
{ "res.timer.data", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_RSFIN }, /* "Td" */
{ "res.timer.hdr", smp_fetch_reX_timers, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV }, /* "Tr" */
{ /* END */ },
}};
INITCALL1(STG_REGISTER, sample_register_fetches, &smp_logs_kws);
/* Note: must not be declared <const> as its list will be overwritten.
MEDIUM: tree-wide: fetches that may return IPV4+IPV6 now return ADDR Historically, the ADDR pseudo-type did not exist. So when IPV6 support was added to existing IPV4 sample fetches (e.g.: src,dst,hdr_ip...) the expected out_type in related sample definitions was left on IPV4 because it was required to declare the out_type as the lowest common denominator (the type that can be casted into all other ones) to make compatibility checks at parse time work properly. However, now that ADDR pseudo-type may safely be used as out_type since ("MEDIUM: sample: add missing ADDR=>? compatibility matrix entries"), we can use ADDR for fetches that may output both IPV4 and IPV6 at runtime. One added benefit on top of making the code less confusing is that 'haproxy -dKsmp' output will now show "addr" instead of "ipv4" for such fetches, so the 'haproxy -dKsmp' output better complies with the fetches signatures from the documentation. out_ip fetch, which returns an ip according to the doc, was purposely left as is (returning IPV4) since the smp_fetch_url_ip() implementation forces output type to IPV4 anyway, and since this is an historical fetch I prefer not to touch it to prevent any regression. However if smp_fetch_url_ip() were to be fixed to also return IPV6 in the future, then its expected out_type may be changed to ADDR as well. Multiple notes in the code were updated to mention that the appropriate pseudo-type may be used instead of the lowest common denominator for out_type when available.
2023-06-07 09:55:13 -04:00
* Note: fetches that may return multiple types should be declared using the
* appropriate pseudo-type. If not available it must be declared as the lowest
* common denominator, the type that can be casted into all other ones.
*/
static struct sample_fetch_kw_list smp_kws = {ILH, {
{ "act_conn", smp_fetch_actconn, 0, NULL, SMP_T_SINT, SMP_USE_CONST },
{ "always_false", smp_fetch_false, 0, NULL, SMP_T_BOOL, SMP_USE_CONST },
{ "always_true", smp_fetch_true, 0, NULL, SMP_T_BOOL, SMP_USE_CONST },
{ "env", smp_fetch_env, ARG1(1,STR), NULL, SMP_T_STR, SMP_USE_CONST },
{ "date", smp_fetch_date, ARG2(0,SINT,STR), smp_check_date_unit, SMP_T_SINT, SMP_USE_CONST },
{ "date_us", smp_fetch_date_us, 0, NULL, SMP_T_SINT, SMP_USE_CONST },
{ "hostname", smp_fetch_hostname, 0, NULL, SMP_T_STR, SMP_USE_CONST },
{ "nbproc", smp_fetch_nbproc,0, NULL, SMP_T_SINT, SMP_USE_CONST },
{ "pid", smp_fetch_pid, 0, NULL, SMP_T_SINT, SMP_USE_CONST },
{ "proc", smp_fetch_proc, 0, NULL, SMP_T_SINT, SMP_USE_CONST },
{ "quic_enabled", smp_fetch_quic_enabled, 0, NULL, SMP_T_BOOL, SMP_USE_CONST },
{ "thread", smp_fetch_thread, 0, NULL, SMP_T_SINT, SMP_USE_CONST },
{ "rand", smp_fetch_rand, ARG1(0,SINT), NULL, SMP_T_SINT, SMP_USE_CONST },
{ "stopping", smp_fetch_stopping, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
{ "uptime", smp_fetch_uptime, 0, NULL, SMP_T_SINT, SMP_USE_CONST },
{ "uuid", smp_fetch_uuid, ARG1(0, SINT), smp_check_uuid, SMP_T_STR, SMP_USE_CONST },
{ "cpu_calls", smp_fetch_cpu_calls, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "cpu_ns_avg", smp_fetch_cpu_ns_avg, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "cpu_ns_tot", smp_fetch_cpu_ns_tot, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "lat_ns_avg", smp_fetch_lat_ns_avg, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "lat_ns_tot", smp_fetch_lat_ns_tot, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
{ "str", smp_fetch_const_str, ARG1(1,STR), NULL , SMP_T_STR, SMP_USE_CONST },
{ "bool", smp_fetch_const_bool, ARG1(1,STR), smp_check_const_bool, SMP_T_BOOL, SMP_USE_CONST },
{ "int", smp_fetch_const_int, ARG1(1,SINT), NULL , SMP_T_SINT, SMP_USE_CONST },
{ "ipv4", smp_fetch_const_ipv4, ARG1(1,IPV4), NULL , SMP_T_IPV4, SMP_USE_CONST },
{ "ipv6", smp_fetch_const_ipv6, ARG1(1,IPV6), NULL , SMP_T_IPV6, SMP_USE_CONST },
{ "bin", smp_fetch_const_bin, ARG1(1,STR), smp_check_const_bin , SMP_T_BIN, SMP_USE_CONST },
{ "meth", smp_fetch_const_meth, ARG1(1,STR), smp_check_const_meth, SMP_T_METH, SMP_USE_CONST },
{ /* END */ },
}};
INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
/* Note: must not be declared <const> as its list will be overwritten */
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
{ "add_item",sample_conv_add_item, ARG3(2,STR,STR,STR), smp_check_add_item, SMP_T_STR, SMP_T_STR },
MEDIUM: sample: introduce 'same' output type Thierry Fournier reported an annoying side-effect when using the debug() converter. Consider the following examples: [1] http-request set-var(txn.test) bool(true),ipmask(24) [2] http-request redirect location /match if { bool(true),ipmask(32) } When starting haproxy with [1] example we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request set-var(txn.test)' rule : converter 'ipmask' cannot be applied. With [2], we get: config : parsing [test.conf:XX] : error detected in frontend 'fe' while parsing 'http-request redirect' rule : error in condition: converter 'ipmask' cannot be applied in ACL expression 'bool(true),ipmask(32)'. Now consider the following examples which are based on [1] and [2] but with the debug() sample conv inserted in-between those incompatible sample types: [1*] http-request set-var(txn.test) bool(true),debug,ipmask(24) [2*] http-request redirect location /match if { bool(true),debug,ipmask(32) } According to the documentation, "it is safe to insert the debug converter anywhere in a chain, even with non-printable sample types". Thus we don't expect any side-effect from using it within a chain. However in current implementation, because of debug() returning SMP_T_ANY type which is a pseudo type (only resolved at runtime), the sample compatibility checks performed at parsing time are completely uneffective. (haproxy will start and no warning will be emitted) The indesirable effect of this is that debug() prevents haproxy from warning you about impossible type conversions, hiding potential errors in the configuration that could result to unexpected evaluation or logic while serving live traffic. We better let haproxy warn you about this kind of errors when it has the chance. With our previous examples, this could cause some inconveniences. Let's say for example that you are testing a configuration prior to deploying it. When testing the config you might want to use debug() converter from time to time to check how the conversion chain behaves. Now after deploying the exact same conf, you might want to remove those testing debug() statements since they are no longer relevant.. but removing them could "break" the config and suddenly prevent haproxy from starting upon reload/restart. (this is the expected behavior, but it comes a bit too late because of debug() hiding errors during tests..) To fix this, we introduce a new output type for sample expressions: SMP_T_SAME - may only be used as "expected" out_type (parsing time) for sample converters. As it name implies, it is a way for the developpers to indicate that the resulting converter's output type is guaranteed to match the type of the sample that is presented on the converter's input side. (converter may alter data content, but data type must not be changed) What it does is that it tells haproxy that if switching to the converter (by looking at the converter's input only, since outype is SAME) is conversion-free, then the converter type can safely be ignored for type compatibility checks within the chain. debug()'s out_type is thus set to SMP_T_SAME instead of ANY, which allows it to fully comply with the doc in the sense that it does not impact the conversion chain when inserted between sample items. Co-authored-by: Thierry Fournier <thierry.f.78@gmail.com>
2023-05-30 10:34:39 -04:00
{ "debug", sample_conv_debug, ARG2(0,STR,STR), smp_check_debug, SMP_T_ANY, SMP_T_SAME },
{ "b64dec", sample_conv_base642bin, 0, NULL, SMP_T_STR, SMP_T_BIN },
{ "base64", sample_conv_bin2base64, 0, NULL, SMP_T_BIN, SMP_T_STR },
{ "concat", sample_conv_concat, ARG3(1,STR,STR,STR), smp_check_concat, SMP_T_STR, SMP_T_STR },
{ "ub64enc", sample_conv_bin2base64url,0, NULL, SMP_T_BIN, SMP_T_STR },
{ "ub64dec", sample_conv_base64url2bin,0, NULL, SMP_T_STR, SMP_T_BIN },
{ "upper", sample_conv_str2upper, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "lower", sample_conv_str2lower, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "length", sample_conv_length, 0, NULL, SMP_T_STR, SMP_T_SINT },
{ "be2dec", sample_conv_be2dec, ARG3(1,STR,SINT,SINT), sample_conv_2dec_check, SMP_T_BIN, SMP_T_STR },
{ "le2dec", sample_conv_le2dec, ARG3(1,STR,SINT,SINT), sample_conv_2dec_check, SMP_T_BIN, SMP_T_STR },
{ "be2hex", sample_conv_be2hex, ARG3(1,STR,SINT,SINT), sample_conv_be2hex_check, SMP_T_BIN, SMP_T_STR },
{ "hex", sample_conv_bin2hex, 0, NULL, SMP_T_BIN, SMP_T_STR },
{ "hex2i", sample_conv_hex2int, 0, NULL, SMP_T_STR, SMP_T_SINT },
{ "ipmask", sample_conv_ipmask, ARG2(1,MSK4,MSK6), NULL, SMP_T_ADDR, SMP_T_ADDR },
{ "ltime", sample_conv_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR },
{ "ms_ltime", sample_conv_ms_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR },
{ "us_ltime", sample_conv_us_ltime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR },
{ "utime", sample_conv_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR },
{ "ms_utime", sample_conv_ms_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR },
{ "us_utime", sample_conv_us_utime, ARG2(1,STR,SINT), NULL, SMP_T_SINT, SMP_T_STR },
{ "crc32", sample_conv_crc32, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "crc32c", sample_conv_crc32c, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "djb2", sample_conv_djb2, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "sdbm", sample_conv_sdbm, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "wt6", sample_conv_wt6, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "xxh3", sample_conv_xxh3, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "xxh32", sample_conv_xxh32, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "xxh64", sample_conv_xxh64, ARG1(0,SINT), NULL, SMP_T_BIN, SMP_T_SINT },
{ "json", sample_conv_json, ARG1(1,STR), sample_conv_json_check, SMP_T_STR, SMP_T_STR },
{ "bytes", sample_conv_bytes, ARG2(1,STR,STR), sample_conv_bytes_check, SMP_T_BIN, SMP_T_BIN },
{ "field", sample_conv_field, ARG3(2,SINT,STR,SINT), sample_conv_field_check, SMP_T_STR, SMP_T_STR },
{ "word", sample_conv_word, ARG3(2,SINT,STR,SINT), sample_conv_field_check, SMP_T_STR, SMP_T_STR },
{ "param", sample_conv_param, ARG2(1,STR,STR), sample_conv_param_check, SMP_T_STR, SMP_T_STR },
{ "regsub", sample_conv_regsub, ARG3(2,REG,STR,STR), sample_conv_regsub_check, SMP_T_STR, SMP_T_STR },
{ "sha1", sample_conv_sha1, 0, NULL, SMP_T_BIN, SMP_T_BIN },
{ "strcmp", sample_conv_strcmp, ARG1(1,STR), smp_check_strcmp, SMP_T_STR, SMP_T_SINT },
{ "host_only", sample_conv_host_only, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "port_only", sample_conv_port_only, 0, NULL, SMP_T_STR, SMP_T_SINT },
/* gRPC converters. */
{ "ungrpc", sample_conv_ungrpc, ARG2(1,PBUF_FNUM,STR), sample_conv_protobuf_check, SMP_T_BIN, SMP_T_BIN },
{ "protobuf", sample_conv_protobuf, ARG2(1,PBUF_FNUM,STR), sample_conv_protobuf_check, SMP_T_BIN, SMP_T_BIN },
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
/* FIX converters */
{ "fix_is_valid", sample_conv_fix_is_valid, 0, NULL, SMP_T_BIN, SMP_T_BOOL },
{ "fix_tag_value", sample_conv_fix_tag_value, ARG1(1,STR), sample_conv_fix_value_check, SMP_T_BIN, SMP_T_BIN },
/* MQTT converters */
{ "mqtt_is_valid", sample_conv_mqtt_is_valid, 0, NULL, SMP_T_BIN, SMP_T_BOOL },
{ "mqtt_field_value", sample_conv_mqtt_field_value, ARG2(2,STR,STR), sample_conv_mqtt_field_value_check, SMP_T_BIN, SMP_T_STR },
{ "iif", sample_conv_iif, ARG2(2, STR, STR), NULL, SMP_T_BOOL, SMP_T_STR },
{ "and", sample_conv_binary_and, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "or", sample_conv_binary_or, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "xor", sample_conv_binary_xor, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "cpl", sample_conv_binary_cpl, 0, NULL, SMP_T_SINT, SMP_T_SINT },
{ "bool", sample_conv_arith_bool, 0, NULL, SMP_T_SINT, SMP_T_BOOL },
{ "not", sample_conv_arith_not, 0, NULL, SMP_T_SINT, SMP_T_BOOL },
{ "odd", sample_conv_arith_odd, 0, NULL, SMP_T_SINT, SMP_T_BOOL },
{ "even", sample_conv_arith_even, 0, NULL, SMP_T_SINT, SMP_T_BOOL },
{ "add", sample_conv_arith_add, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "sub", sample_conv_arith_sub, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "mul", sample_conv_arith_mul, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "div", sample_conv_arith_div, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "mod", sample_conv_arith_mod, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "neg", sample_conv_arith_neg, 0, NULL, SMP_T_SINT, SMP_T_SINT },
MEDIUM: samples: provide basic arithmetic and bitwise operators This commit introduces a new category of converters. They are bitwise and arithmetic operators which support performing basic operations on integers. Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic operations are supported (add, sub, mul, div, mod, neg). Some comparators are provided (odd, even, not, bool) which make it possible to report a match without having to write an ACL. The detailed list of new operators as they appear in the doc is : add(<value>) Adds <value> to the input value of type unsigned integer, and returns the result as an unsigned integer. and(<value>) Performs a bitwise "AND" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. bool Returns a boolean TRUE if the input value of type unsigned integer is non-null, otherwise returns FALSE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the presence of a flag). cpl Takes the input value of type unsigned integer, applies a twos-complement (flips all bits) and returns the result as an unsigned integer. div(<value>) Divides the input value of type unsigned integer by <value>, and returns the result as an unsigned integer. If <value> is null, the largest unsigned integer is returned (typically 2^32-1). even Returns a boolean TRUE if the input value of type unsigned integer is even otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool". mod(<value>) Divides the input value of type unsigned integer by <value>, and returns the remainder as an unsigned integer. If <value> is null, then zero is returned. mul(<value>) Multiplies the input value of type unsigned integer by <value>, and returns the product as an unsigned integer. In case of overflow, the higher bits are lost, leading to seemingly strange values. neg Takes the input value of type unsigned integer, computes the opposite value, and returns the remainder as an unsigned integer. 0 is identity. This operator is provided for reversed subtracts : in order to subtract the input from a constant, simply perform a "neg,add(value)". not Returns a boolean FALSE if the input value of type unsigned integer is non-null, otherwise returns TRUE. Used in conjunction with and(), it can be used to report true/false for bit testing on input values (eg: verify the absence of a flag). odd Returns a boolean TRUE if the input value of type unsigned integer is odd otherwise returns FALSE. It is functionally equivalent to "and(1),bool". or(<value>) Performs a bitwise "OR" between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer. sub(<value>) Subtracts <value> from the input value of type unsigned integer, and returns the result as an unsigned integer. Note: in order to subtract the input from a constant, simply perform a "neg,add(value)". xor(<value>) Performs a bitwise "XOR" (exclusive OR) between <value> and the input value of type unsigned integer, and returns the result as an unsigned integer.
2015-01-27 09:12:13 -05:00
{ "htonl", sample_conv_htonl, 0, NULL, SMP_T_SINT, SMP_T_BIN },
{ "cut_crlf", sample_conv_cut_crlf, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "ltrim", sample_conv_ltrim, ARG1(1,STR), NULL, SMP_T_STR, SMP_T_STR },
{ "rtrim", sample_conv_rtrim, ARG1(1,STR), NULL, SMP_T_STR, SMP_T_STR },
{ "json_query", sample_conv_json_query, ARG2(1,STR,STR), sample_check_json_query , SMP_T_STR, SMP_T_ANY },
#ifdef USE_OPENSSL
/* JSON Web Token converters */
{ "jwt_header_query", sample_conv_jwt_header_query, ARG2(0,STR,STR), sample_conv_jwt_query_check, SMP_T_BIN, SMP_T_ANY },
{ "jwt_payload_query", sample_conv_jwt_payload_query, ARG2(0,STR,STR), sample_conv_jwt_query_check, SMP_T_BIN, SMP_T_ANY },
{ "jwt_verify", sample_conv_jwt_verify, ARG2(2,STR,STR), sample_conv_jwt_verify_check, SMP_T_BIN, SMP_T_SINT },
#endif
{ "when", sample_conv_when, ARG3(1,STR,STR,STR), check_when_cond, SMP_T_ANY, SMP_T_ANY },
{ NULL, NULL, 0, 0, 0 },
}};
INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);