[MEDIUM] Create new protected pattern types CONSTSTRING and CONSTDATA to force memcpy if data from protected areas need to be manipulated.
Enhance pattern convs and fetch argument parsing, now fetchs and convs callbacks used typed args.
Add more details on error messages on parsing pattern expression function.
Update existing pattern convs and fetchs to new proto.
Create stick table key type "binary".
Manage Truncation and padding if pattern's fetch-converted result don't match table key size.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 1cd7d39..3841c99 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2728,9 +2728,9 @@
goto out;
}
- expr = pattern_parse_expr(args, &myidx);
+ expr = pattern_parse_expr(args, &myidx, trash, sizeof(trash));
if (!expr) {
- Alert("parsing [%s:%d] : '%s': unknown fetch method '%s'.\n", file, linenum, args[0], args[myidx]);
+ Alert("parsing [%s:%d] : '%s': %s\n", file, linenum, args[0], trash);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
diff --git a/src/pattern.c b/src/pattern.c
index 6a12442..ba8d5a0 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -14,6 +14,7 @@
#include <arpa/inet.h>
#include <proto/pattern.h>
+#include <proto/buffers.h>
#include <common/standard.h>
/* static structure used on pattern_process if <p> is NULL*/
@@ -109,9 +110,7 @@
else
pattern_trash_buf = pattern_trash_buf1;
- trash_chunk.str = pattern_trash_buf;
- trash_chunk.len = 0;
- trash_chunk.size = BUFSIZE;
+ chunk_init(&trash_chunk, pattern_trash_buf, BUFSIZE);
return &trash_chunk;
}
@@ -121,9 +120,7 @@
*/
static void pattern_data_setstring(union pattern_data *data, struct chunk *c)
{
- data->str.str = c->str;
- data->str.len = c->len;
- data->str.size = c->size;
+ chunk_initlen(&data->str, c->str, c->size, c->len);
}
/******************************************************************/
@@ -172,6 +169,7 @@
if (!pos)
return 0;
+ trash->size = trash->size - (pos - trash->str);
trash->str = pos;
trash->len = strlen(pos);
@@ -180,6 +178,19 @@
return 1;
}
+static int c_datadup(union pattern_data *data)
+{
+ struct chunk *trash = get_trash_chunk();
+
+ trash->len = data->str.len < trash->size ? data->str.len : trash->size;
+ memcpy(trash->str, data->str.str, trash->len);
+
+ pattern_data_setstring(data, trash);
+
+ return 1;
+}
+
+
static int c_donothing(union pattern_data *data)
{
return 1;
@@ -211,9 +222,13 @@
typedef int (*pattern_cast_fct)(union pattern_data *data);
static pattern_cast_fct pattern_casts[PATTERN_TYPES][PATTERN_TYPES] = {
- { c_donothing, c_ip2int, c_ip2str },
- { c_int2ip, c_donothing, c_int2str },
- { c_str2ip, c_str2int, c_donothing },
+/* to: IP INTEGER STRING DATA CONSTSTRING CONSTDATA */
+/* from: IP */ { c_donothing, c_ip2int, c_ip2str, NULL, c_ip2str, NULL },
+/* INTEGER */ { c_int2ip, c_donothing, c_int2str, NULL, c_int2str, NULL },
+/* STRING */ { c_str2ip, c_str2int, c_donothing, c_donothing, c_donothing, c_donothing },
+/* DATA */ { NULL, NULL, NULL, c_donothing, NULL, c_donothing },
+/* CONSTSTRING */ { c_str2ip, c_str2int, c_datadup, c_datadup, c_donothing, c_donothing },
+/* CONSTDATA */ { NULL, NULL, NULL, c_datadup, NULL, NULL },
};
@@ -222,7 +237,7 @@
* fetch keyword followed by format conversion keywords.
* Returns a pointer on allocated pattern expression structure.
*/
-struct pattern_expr *pattern_parse_expr(char **str, int *idx)
+struct pattern_expr *pattern_parse_expr(char **str, int *idx, char *err, int err_size)
{
const char *endw;
const char *end;
@@ -230,34 +245,88 @@
struct pattern_fetch *fetch;
struct pattern_conv *conv;
unsigned long prev_type;
+ char *p;
- if (!str[*idx])
+ snprintf(err, err_size, "memory error.");
+ if (!str[*idx]) {
+
+ snprintf(err, err_size, "missing fetch method.");
goto out_error;
+ }
end = str[*idx] + strlen(str[*idx]);
endw = strchr(str[*idx], '(');
if (!endw)
endw = end;
- else if ((end-1)[0] != ')')
+ else if ((end-1)[0] != ')') {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "syntax error: missing ')' after keyword '%s'.", p);
+ free(p);
+ }
goto out_error;
+ }
fetch = find_pattern_fetch(str[*idx], endw - str[*idx]);
- if (!fetch)
+ if (!fetch) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "unknown fetch method '%s'.", p);
+ free(p);
+ }
goto out_error;
+ }
+ if (fetch->out_type >= PATTERN_TYPES) {
- if (fetch->out_type >= PATTERN_TYPES)
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "returns type of fetch method '%s' is unknown.", p);
+ free(p);
+ }
goto out_error;
+ }
prev_type = fetch->out_type;
expr = calloc(1, sizeof(struct pattern_expr));
+ if (!expr)
+ goto out_error;
LIST_INIT(&(expr->conv_exprs));
expr->fetch = fetch;
if (end != endw) {
- expr->arg_len = end - endw - 2;
- expr->arg = my_strndup(endw + 1, expr->arg_len);
+ int i = end - endw - 2;
+
+ if (!fetch->parse_args) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "fetch method '%s' does not support any args.", p);
+ free(p);
+ }
+ goto out_error;
+ }
+ p = my_strndup(endw + 1, i);
+ if (!p)
+ goto out_error;
+ i = fetch->parse_args(p, &expr->arg_p, &expr->arg_i);
+ free(p);
+ if (!i) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "invalid args in fetch method '%s'.", p);
+ free(p);
+ }
+ goto out_error;
+ }
+ }
+ else if (fetch->parse_args) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "missing args for fetch method '%s'.", p);
+ free(p);
+ }
+ goto out_error;
}
for (*idx += 1; *(str[*idx]); (*idx)++) {
@@ -268,42 +337,85 @@
if (!endw)
endw = end;
- else if ((end-1)[0] != ')')
+ else if ((end-1)[0] != ')') {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "syntax error, missing ')' after keyword '%s'.", p);
+ free(p);
+ }
goto out_error;
+ }
conv = find_pattern_conv(str[*idx], endw - str[*idx]);
if (!conv)
break;
if (conv->in_type >= PATTERN_TYPES ||
- conv->out_type >= PATTERN_TYPES)
+ conv->out_type >= PATTERN_TYPES) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "returns type of conv method '%s' is unknown.", p);
+ free(p);
+ }
goto out_error;
+ }
/* If impossible type conversion */
- if (!pattern_casts[prev_type][conv->in_type])
+ if (!pattern_casts[prev_type][conv->in_type]) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "conv method '%s' cannot be applied.", p);
+ free(p);
+ }
goto out_error;
+ }
prev_type = conv->out_type;
conv_expr = calloc(1, sizeof(struct pattern_conv_expr));
+ if (!conv_expr)
+ goto out_error;
LIST_ADDQ(&(expr->conv_exprs), &(conv_expr->list));
conv_expr->conv = conv;
if (end != endw) {
int i = end - endw - 2;
- char *p = my_strndup(endw + 1, i);
+
+ if (!conv->parse_args) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+
+ if (p) {
+ snprintf(err, err_size, "conv method '%s' does not support any args.", p);
+ free(p);
+ }
+ goto out_error;
+ }
- if (conv->parse_args) {
- i = conv->parse_args(p, &conv_expr->arg_p, &conv_expr->arg_i);
+ p = my_strndup(endw + 1, i);
+ if (!p)
+ goto out_error;
+ i = conv->parse_args(p, &conv_expr->arg_p, &conv_expr->arg_i);
+ free(p);
+ if (!i) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "invalid args in conv method '%s'.", p);
+ free(p);
+ }
+ goto out_error;
+ }
+ }
+ else if (conv->parse_args) {
+ p = my_strndup(str[*idx], endw - str[*idx]);
+ if (p) {
+ snprintf(err, err_size, "missing args for conv method '%s'.", p);
free(p);
- if (!i)
- goto out_error;
- } else {
- conv_expr->arg_i = i;
- conv_expr->arg_p = p;
}
+ goto out_error;
}
+
}
+
return expr;
out_error:
@@ -327,7 +439,7 @@
if (p == NULL)
p = &spattern;
- if (!expr->fetch->process(px, l4, l7, dir, expr->arg, expr->arg_len, &p->data))
+ if (!expr->fetch->process(px, l4, l7, dir, expr->arg_p, expr->arg_i, &p->data))
return NULL;
p->type = expr->fetch->out_type;
@@ -345,28 +457,49 @@
return p;
}
-/* Converts an argument string to an IPv4 mask stored in network byte order in
- * arg_i. Returns non-zero in case of success, 0 on error.
+/* Converts an argument string mask to a pattern_arg type IP.
+ * Returns non-zero in case of success, 0 on error.
*/
-static int pattern_conv_arg_to_ipmask(const char *arg_str, void **arg_p, int *arg_i)
+int pattern_arg_ipmask(const char *arg_str, struct pattern_arg **arg_p, int *arg_i)
{
- struct in_addr mask;
+ *arg_i = 1;
+ *arg_p = calloc(1, *arg_i*sizeof(struct pattern_arg));
+ (*arg_p)->type = PATTERN_ARG_TYPE_IP;
- if (!str2mask(arg_str, &mask))
+ if (!str2mask(arg_str, &(*arg_p)->data.ip))
return 0;
- *arg_i = mask.s_addr;
return 1;
}
+
+/* Converts an argument string to a pattern_arg type STRING.
+ * Returns non-zero in case of success, 0 on error.
+ */
+int pattern_arg_str(const char *arg_str, struct pattern_arg **arg_p, int *arg_i)
+{
+ *arg_i = 1;
+ *arg_p = calloc(1, *arg_i*sizeof(struct pattern_arg));
+ (*arg_p)->type = PATTERN_ARG_TYPE_STRING;
+ (*arg_p)->data.str.str = strdup(arg_str);
+ (*arg_p)->data.str.len = strlen(arg_str);
+
+
+ return 1;
+}
+
+
/*****************************************************************/
/* Pattern format convert functions */
/*****************************************************************/
-static int pattern_conv_str2lower(const void *arg_p, int arg_i, union pattern_data *data)
+static int pattern_conv_str2lower(const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
{
int i;
+ if (!data->str.size)
+ return 0;
+
for (i = 0; i < data->str.len; i++) {
if ((data->str.str[i] >= 'A') && (data->str.str[i] <= 'Z'))
data->str.str[i] += 'a' - 'A';
@@ -374,10 +507,13 @@
return 1;
}
-static int pattern_conv_str2upper(const void *arg_p, int arg_i, union pattern_data *data)
+static int pattern_conv_str2upper(const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
{
int i;
+ if (!data->str.size)
+ return 0;
+
for (i = 0; i < data->str.len; i++) {
if ((data->str.str[i] >= 'a') && (data->str.str[i] <= 'z'))
data->str.str[i] += 'A' - 'a';
@@ -386,7 +522,7 @@
}
/* takes the netmask in arg_i */
-static int pattern_conv_ipmask(const void *arg_p, int arg_i, union pattern_data *data)
+static int pattern_conv_ipmask(const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
{
data->ip.s_addr &= arg_i;
return 1;
@@ -394,10 +530,10 @@
/* Note: must not be declared <const> as its list will be overwritten */
static struct pattern_conv_kw_list pattern_conv_kws = {{ },{
- { "upper", pattern_conv_str2upper, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
- { "lower", pattern_conv_str2lower, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
- { "ipmask", pattern_conv_ipmask, PATTERN_TYPE_IP, PATTERN_TYPE_IP, pattern_conv_arg_to_ipmask },
- { NULL, NULL, 0, 0 },
+ { "upper", pattern_conv_str2upper, NULL, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
+ { "lower", pattern_conv_str2lower, NULL, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
+ { "ipmask", pattern_conv_ipmask, pattern_arg_ipmask, PATTERN_TYPE_IP, PATTERN_TYPE_IP },
+ { NULL, NULL, NULL, 0, 0 },
}};
__attribute__((constructor))
diff --git a/src/proto_http.c b/src/proto_http.c
index 29d8f51..db86769 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -8105,21 +8105,23 @@
*/
static int
pattern_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, int dir,
- const char *arg, int arg_len, union pattern_data *data)
+ const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
{
struct http_txn *txn = l7;
- data->ip.s_addr = htonl(get_ip_from_hdr2(&txn->req, arg, arg_len, &txn->hdr_idx, -1));
+ data->ip.s_addr = htonl(get_ip_from_hdr2(&txn->req, arg_p->data.str.str, arg_p->data.str.len, &txn->hdr_idx, -1));
return data->ip.s_addr != 0;
}
+
+
/************************************************************************/
/* All supported keywords must be declared here. */
/************************************************************************/
/* Note: must not be declared <const> as its list will be overwritten */
static struct pattern_fetch_kw_list pattern_fetch_keywords = {{ },{
- { "hdr", pattern_fetch_hdr_ip, PATTERN_TYPE_IP, PATTERN_FETCH_REQ },
- { NULL, NULL, 0, 0 },
+ { "hdr", pattern_fetch_hdr_ip, pattern_arg_str, PATTERN_TYPE_IP, PATTERN_FETCH_REQ },
+ { NULL, NULL, NULL, 0, 0 },
}};
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index f58d264..380136f 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1244,7 +1244,7 @@
/* extract the connection's source address */
static int
pattern_fetch_src(struct proxy *px, struct session *l4, void *l7, int dir,
- const char *arg, int arg_len, union pattern_data *data)
+ const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
{
if (l4->cli_addr.ss_family != AF_INET )
return 0;
@@ -1295,7 +1295,7 @@
/* extract the connection's destination address */
static int
pattern_fetch_dst(struct proxy *px, struct session *l4, void *l7, int dir,
- const char *arg, int arg_len, union pattern_data *data)
+ const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
{
if (!(l4->flags & SN_FRT_ADDR_SET))
get_frt_addr(l4);
@@ -1328,8 +1328,7 @@
static int
pattern_fetch_dport(struct proxy *px, struct session *l4, void *l7, int dir,
- const char *arg, int arg_len, union pattern_data *data)
-
+ const struct pattern_arg *arg, int i, union pattern_data *data)
{
if (!(l4->flags & SN_FRT_ADDR_SET))
get_frt_addr(l4);
@@ -1358,10 +1357,10 @@
/* Note: must not be declared <const> as its list will be overwritten */
static struct pattern_fetch_kw_list pattern_fetch_keywords = {{ },{
- { "src", pattern_fetch_src, PATTERN_TYPE_IP, PATTERN_FETCH_REQ },
- { "dst", pattern_fetch_dst, PATTERN_TYPE_IP, PATTERN_FETCH_REQ },
- { "dst_port", pattern_fetch_dport, PATTERN_TYPE_INTEGER, PATTERN_FETCH_REQ },
- { NULL, NULL, 0, 0 },
+ { "src", pattern_fetch_src, NULL, PATTERN_TYPE_IP, PATTERN_FETCH_REQ },
+ { "dst", pattern_fetch_dst, NULL, PATTERN_TYPE_IP, PATTERN_FETCH_REQ },
+ { "dst_port", pattern_fetch_dport, NULL, PATTERN_TYPE_INTEGER, PATTERN_FETCH_REQ },
+ { NULL, NULL, NULL, 0, 0 },
}};
__attribute__((constructor))
diff --git a/src/session.c b/src/session.c
index 53ee397..4d2b605 100644
--- a/src/session.c
+++ b/src/session.c
@@ -1050,7 +1050,7 @@
if (ret) {
struct stktable_key *key;
- key = stktable_fetch_key(px, s, &s->txn, PATTERN_FETCH_REQ, rule->expr, rule->table.t->type);
+ key = stktable_fetch_key(rule->table.t, px, s, &s->txn, PATTERN_FETCH_REQ, rule->expr);
if (!key)
continue;
@@ -1143,7 +1143,7 @@
if (ret) {
struct stktable_key *key;
- key = stktable_fetch_key(px, s, &s->txn, PATTERN_FETCH_RTR, rule->expr, rule->table.t->type);
+ key = stktable_fetch_key(rule->table.t, px, s, &s->txn, PATTERN_FETCH_RTR, rule->expr);
if (!key)
continue;
diff --git a/src/stick_table.c b/src/stick_table.c
index cb9b6b3..bf8df0f 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -181,7 +181,7 @@
struct ebmb_node *eb;
if (t->type == STKTABLE_TYPE_STRING)
- eb = ebst_lookup_len(&t->keys, key->key, key->key_len);
+ eb = ebst_lookup_len(&t->keys, key->key, key->key_len+1 < t->key_size ? key->key_len : t->key_size-1);
else
eb = ebmb_lookup(&t->keys, key->key, t->key_size);
@@ -383,9 +383,10 @@
/*
* Configuration keywords of known table types
*/
-struct stktable_type stktable_types[STKTABLE_TYPES] = { { "ip", 0, 4 } ,
+struct stktable_type stktable_types[STKTABLE_TYPES] = {{ "ip", 0, 4 },
{ "integer", 0, 4 },
- { "string", STK_F_CUSTOM_KEYSIZE, 32 } };
+ { "string", STK_F_CUSTOM_KEYSIZE, 32 },
+ { "binary", STK_F_CUSTOM_KEYSIZE, 32 } };
/*
@@ -406,10 +407,12 @@
if (strcmp("len", args[*myidx]) == 0) {
(*myidx)++;
*key_size = atol(args[*myidx]);
- if ( !*key_size )
+ if (!*key_size)
break;
- /* null terminated string needs +1 for '\0'. */
- (*key_size)++;
+ if (*type == STKTABLE_TYPE_STRING) {
+ /* null terminated string needs +1 for '\0'. */
+ (*key_size)++;
+ }
(*myidx)++;
}
}
@@ -504,9 +507,13 @@
typedef void *(*pattern_to_key_fct)(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len);
static pattern_to_key_fct pattern_to_key[PATTERN_TYPES][STKTABLE_TYPES] = {
- { k_ip2ip, k_ip2int, k_ip2str },
- { k_int2ip, k_int2int, k_int2str },
- { k_str2ip, k_str2int, k_str2str },
+/* table type: IP INTEGER STRING BINARY */
+/* pattern type: IP */ { k_ip2ip, k_ip2int, k_ip2str, NULL },
+/* INTEGER */ { k_int2ip, k_int2int, k_int2str, NULL },
+/* STRING */ { k_str2ip, k_str2int, k_str2str, k_str2str },
+/* DATA */ { NULL, NULL, NULL, k_str2str },
+/* CONSTSTRING */ { k_str2ip, k_str2int, k_str2str, k_str2str },
+/* CONSTDATA */ { NULL, NULL, NULL, k_str2str },
};
@@ -516,8 +523,8 @@
* no key could be extracted, or a pointer to the converted result stored in
* static_table_key in format <table_type>.
*/
-struct stktable_key *stktable_fetch_key(struct proxy *px, struct session *l4, void *l7, int dir,
- struct pattern_expr *expr, unsigned long table_type)
+struct stktable_key *stktable_fetch_key(struct stktable *t, struct proxy *px, struct session *l4, void *l7, int dir,
+ struct pattern_expr *expr)
{
struct pattern *ptrn;
@@ -525,12 +532,39 @@
if (!ptrn)
return NULL;
- static_table_key.key_len = (size_t)-1;
- static_table_key.key = pattern_to_key[ptrn->type][table_type](&ptrn->data, &static_table_key.data, &static_table_key.key_len);
+ static_table_key.key_len = t->key_size;
+ static_table_key.key = pattern_to_key[ptrn->type][t->type](&ptrn->data, &static_table_key.data, &static_table_key.key_len);
if (!static_table_key.key)
return NULL;
+ if ((static_table_key.key_len < t->key_size) && (t->type != STKTABLE_TYPE_STRING)) {
+ /* need padding with null */
+
+ /* assume static_table_key.key_len is less than sizeof(static_table_key.data.buf)
+ cause t->key_size is necessary less than sizeof(static_table_key.data) */
+
+ if ((char *)static_table_key.key > (char *)&static_table_key.data &&
+ (char *)static_table_key.key < (char *)&static_table_key.data + sizeof(static_table_key.data)) {
+ /* key buffer is part of the static_table_key private data buffer, but is not aligned */
+
+ if (sizeof(static_table_key.data) - ((char *)static_table_key.key - (char *)&static_table_key.data) < t->key_size) {
+ /* if not remain enougth place for padding , process a realign */
+ memmove(static_table_key.data.buf, static_table_key.key, static_table_key.key_len);
+ static_table_key.key = static_table_key.data.buf;
+ }
+ }
+ else if (static_table_key.key != static_table_key.data.buf) {
+ /* key definitly not part of the static_table_key private data buffer */
+
+ memcpy(static_table_key.data.buf, static_table_key.key, static_table_key.key_len);
+ static_table_key.key = static_table_key.data.buf;
+ }
+
+ memset(static_table_key.key + static_table_key.key_len, 0, t->key_size - static_table_key.key_len);
+ }
+
+
return &static_table_key;
}