diff --git a/Makefile b/Makefile index daead76ad..0c9df3fb6 100644 --- a/Makefile +++ b/Makefile @@ -464,7 +464,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \ src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o \ src/stream_interface.o src/dumpstats.o src/proto_tcp.o \ src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \ - src/acl.o src/memory.o src/freq_ctr.o + src/acl.o src/pattern.o src/memory.o src/freq_ctr.o EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \ $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \ diff --git a/include/proto/pattern.h b/include/proto/pattern.h new file mode 100644 index 000000000..41eda06e9 --- /dev/null +++ b/include/proto/pattern.h @@ -0,0 +1,39 @@ +/* + * include/proto/pattern.h + * Functions for patterns management. + * + * Copyright (C) 2009-2010 EXCELIANCE, Emeric Brun + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PROTO_PATTERN_H +#define _PROTO_PATTERN_H + +#include +#include + +struct pattern_expr *pattern_parse_expr(char **str, int *idx); +struct pattern *pattern_process(struct proxy *px, struct session *l4, + void *l7, int dir, struct pattern_expr *expr, + struct pattern *p); +struct stktable_key *pattern_process_key(struct proxy *px, struct session *l4, + void *l7, int dir, struct pattern_expr *expr, + unsigned long table_type); +int pattern_notusable_key(struct pattern_expr *expr, unsigned long table_type); +void pattern_register_fetches(struct pattern_fetch_kw_list *psl); +void pattern_register_convs(struct pattern_conv_kw_list *psl); + +#endif diff --git a/include/types/pattern.h b/include/types/pattern.h new file mode 100644 index 000000000..c6f8ec8a5 --- /dev/null +++ b/include/types/pattern.h @@ -0,0 +1,106 @@ +/* + * include/types/pattern.h + * Macros, variables and structures for patterns management. + * + * Copyright (C) 2009-2010 EXCELIANCE, Emeric Brun + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _TYPES_PATTERN_H +#define _TYPES_PATTERN_H + +#include +#include +#include + +/* pattern in and out types */ +enum { + PATTERN_TYPE_IP = 0, /* ipv4 type */ + PATTERN_TYPE_INTEGER = 1, /* unsigned 32bits integer type */ + PATTERN_TYPE_STRING = 2, /* char string type */ + PATTERN_TYPES +}; + +/* pattern fetch direction */ +#define PATTERN_FETCH_REQ 1 +#define PATTERN_FETCH_RTR 2 + +/* pattern result data */ +union pattern_data { + struct in_addr ip; /* used for ipv4 type */ + uint32_t integer; /* used for unsigned 32bits integer type */ + struct chunk str; /* used for char string type */ +}; + +/* pattern result */ +struct pattern { + int type; /* current type of data */ + union pattern_data data; /* data */ +}; + +/* pattern conversion */ +struct pattern_conv { + const char *kw; /* configuration keyword */ + int (*process)(const char *arg, + int arg_len, + union pattern_data *data); /* process function */ + unsigned int in_type; /* input needed pattern type */ + unsigned int out_type; /* output pattern type */ +}; + +/* pattern conversion expression */ +struct pattern_conv_expr { + struct list list; /* member of a pattern expression */ + struct pattern_conv *conv; /* pattern conversion */ + char *arg; /* configured keyword argument */ + int arg_len; /* configured keyword argument length */ +}; + +/* pattern fetch */ +struct pattern_fetch { + const char *kw; /* configuration keyword */ + int (*process)(struct proxy *px, + struct session *l4, + void *l7, + int dir, const char *arg, + int arg_len, + union pattern_data *data); /* fetch processing function */ + unsigned long out_type; /* output pattern type */ + int dir; /* usable directions */ +}; + +/* pattern expression */ +struct pattern_expr { + struct list list; /* member of list of pattern, currently not used */ + struct pattern_fetch *fetch; /* pattern fetch */ + char *arg; /* configured keyword argument */ + int arg_len; /* configured keyword argument length */ + struct list conv_exprs; /* list of conversion expression to apply */ +}; + +/* pattern fetch keywords list */ +struct pattern_fetch_kw_list { + struct list list; /* head of pattern fetch keyword list */ + struct pattern_fetch kw[VAR_ARRAY]; /* array of pattern fetches */ +}; + +/* pattern conversion keywords list */ +struct pattern_conv_kw_list { + struct list list; /* head of pattern conversion keyword list */ + struct pattern_conv kw[VAR_ARRAY]; /* array of pattern ions */ +}; + +#endif /* _TYPES_PATTERN_H */ diff --git a/src/pattern.c b/src/pattern.c new file mode 100644 index 000000000..4270cde30 --- /dev/null +++ b/src/pattern.c @@ -0,0 +1,569 @@ +/* + * Patterns management functions. + * + * Copyright 2009-2010 EXCELIANCE, Emeric Brun + * + * 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. + * + */ + +#include +#include +#include + +#include +#include + +/* static structure used on pattern_process if

is NULL*/ +static struct pattern spattern; + +/* trash chunk used for pattern conversions */ +static struct chunk trash_chunk; + +/* trash buffers used or pattern conversions */ +static char pattern_trash_buf1[BUFSIZE]; +static char pattern_trash_buf2[BUFSIZE]; + +/* pattern_trash_buf point on used buffer*/ +static char *pattern_trash_buf = pattern_trash_buf1; + +/* static structure used to returns builded table key from a pattern*/ +static struct stktable_key stable_key; + +/* list head of all known pattern fetch keywords */ +static struct pattern_fetch_kw_list pattern_fetches = { + .list = LIST_HEAD_INIT(pattern_fetches.list) +}; + +/* list head of all known pattern format conversion keywords */ +static struct pattern_conv_kw_list pattern_convs = { + .list = LIST_HEAD_INIT(pattern_convs.list) +}; + +/* + * Registers the pattern fetch keyword list as a list of valid keywords for next + * parsing sessions. + */ +void pattern_register_fetches(struct pattern_fetch_kw_list *pfkl) +{ + LIST_ADDQ(&pattern_fetches.list, &pfkl->list); +} + +/* + * Registers the pattern format coverstion keyword list as a list of valid keywords for next + * parsing sessions. + */ +void pattern_register_convs(struct pattern_conv_kw_list *pckl) +{ + LIST_ADDQ(&pattern_convs.list, &pckl->list); +} + +/* + * Returns the pointer on pattern fetch keyword structure identified by + * string of in buffer . + * + */ +struct pattern_fetch *find_pattern_fetch(const char *kw, int len) +{ + int index; + struct pattern_fetch_kw_list *kwl; + + list_for_each_entry(kwl, &pattern_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; +} + +/* + * Returns the pointer on pattern format conversion keyword structure identified by + * string of in buffer . + * + */ +struct pattern_conv *find_pattern_conv(const char *kw, int len) +{ + int index; + struct pattern_conv_kw_list *kwl; + + list_for_each_entry(kwl, &pattern_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; +} + + +/* +* Returns a static trash struct chunk to use in pattern casts or format conversions +* Swiths the 2 available trash buffers to protect data during convert +*/ +static struct chunk *get_trash_chunk(void) +{ + if (pattern_trash_buf == pattern_trash_buf1) + pattern_trash_buf = pattern_trash_buf2; + else + pattern_trash_buf = pattern_trash_buf1; + + trash_chunk.str = pattern_trash_buf; + trash_chunk.len = 0; + trash_chunk.size = BUFSIZE; + + return &trash_chunk; +} + +/* +* Used to set pattern data from a struct chunk, could be the trash struct chunk +*/ +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; +} + +/******************************************************************/ +/* Pattern casts functions */ +/******************************************************************/ + +static int c_ip2int(union pattern_data *data) +{ + data->integer = ntohl(data->ip.s_addr); + return 1; +} + +static int c_ip2str(union pattern_data *data) +{ + struct chunk *trash = get_trash_chunk(); + + if (!inet_ntop(AF_INET, (void *)&data->ip, trash->str, trash->size)) + return 0; + + trash->len = strlen(trash->str); + pattern_data_setstring(data, trash); + + return 1; +} + +static int c_int2ip(union pattern_data *data) +{ + data->ip.s_addr = htonl(data->integer); + return 1; +} + +/* Convert a fixed-length string to an IP address. Returns 0 in case of error, + * or the number of chars read in case of success. + */ +static int buf2ip(const char *buf, size_t len, struct in_addr *dst) +{ + const char *addr; + int saw_digit, octets, ch; + u_char tmp[4], *tp; + const char *cp = buf; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + + for (addr = buf; addr - buf < len; addr++) { + unsigned char digit = (ch = *addr) - '0'; + + if (digit > 9 && ch != '.') + break; + + if (digit <= 9) { + u_int new = *tp * 10 + digit; + + if (new > 255) + return 0; + + *tp = new; + + if (!saw_digit) { + if (++octets > 4) + return 0; + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return 0; + + *++tp = 0; + saw_digit = 0; + } else + return 0; + } + + if (octets < 4) + return 0; + + memcpy(&dst->s_addr, tmp, 4); + return addr - cp; +} + +static int c_str2ip(union pattern_data *data) +{ + if (!buf2ip(data->str.str, data->str.len, &data->ip)) + return 0; + return 1; +} + +static int c_int2str(union pattern_data *data) +{ + struct chunk *trash = get_trash_chunk(); + char *pos; + + pos = ultoa_r(data->integer, trash->str, trash->size); + + if (!pos) + return 0; + + trash->str = pos; + trash->len = strlen(pos); + + pattern_data_setstring(data, trash); + + return 1; +} + +static int c_donothing(union pattern_data *data) +{ + return 1; +} + +static int c_str2int(union pattern_data *data) +{ + int i; + uint32_t ret = 0; + + for (i = 0; i < data->str.len; i++) { + uint32_t val = data->str.str[i] - '0'; + + if (val > 9) + break; + + ret = ret * 10 + val; + } + + data->integer = ret; + return 1; +} + +/*****************************************************************/ +/* Pattern casts matrix: */ +/* pattern_casts[from type][to type] */ +/* NULL pointer used for impossible pattern casts */ +/*****************************************************************/ + +typedef int (*pattern_cast)(union pattern_data *data); +static pattern_cast pattern_casts[PATTERN_TYPES][PATTERN_TYPES] = { { c_donothing, c_ip2int, c_ip2str }, + { c_int2ip, c_donothing, c_int2str }, + { c_str2ip, c_str2int, c_donothing } }; + + +/*****************************************************************/ +/* typed pattern to typed table key functions */ +/*****************************************************************/ + +static void *k_int2int(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + return (void *)&pdata->integer; +} + +static void *k_ip2ip(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + return (void *)&pdata->ip.s_addr; +} + +static void *k_ip2int(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + kdata->integer = ntohl(pdata->ip.s_addr); + return (void *)&kdata->integer; +} + +static void *k_int2ip(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + kdata->ip.s_addr = htonl(pdata->integer); + return (void *)&kdata->ip.s_addr; +} + +static void *k_str2str(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + *len = pdata->str.len; + return (void *)pdata->str.str; +} + +static void *k_ip2str(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + if (!inet_ntop(AF_INET, &pdata->ip, kdata->buf, sizeof(kdata->buf))) + return NULL; + + *len = strlen((const char *)kdata->buf); + return (void *)kdata->buf; +} + +static void *k_int2str(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + void *key; + + key = (void *)ultoa_r(pdata->integer, kdata->buf, sizeof(kdata->buf)); + if (!key) + return NULL; + + *len = strlen((const char *)key); + return key; +} + +static void *k_str2ip(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + if (!buf2ip(pdata->str.str, pdata->str.len, &kdata->ip)) + return NULL; + + return (void *)&kdata->ip.s_addr; +} + + +static void *k_str2int(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len) +{ + int i; + + kdata->integer = 0; + for (i = 0; i < pdata->str.len; i++) { + uint32_t val = pdata->str.str[i] - '0'; + + if (val > 9) + break; + + kdata->integer = kdata->integer * 10 + val; + } + return (void *)&kdata->integer; +} + +/*****************************************************************/ +/* typed pattern to typed table key matrix: */ +/* pattern_keys[from pattern type][to table key type] */ +/* NULL pointer used for impossible pattern casts */ +/*****************************************************************/ + +typedef void *(*pattern_key)(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len); +static pattern_key pattern_keys[PATTERN_TYPES][STKTABLE_TYPES] = { { k_ip2ip, k_ip2int, k_ip2str }, + { k_int2ip, k_int2int, k_int2str }, + { k_str2ip, k_str2int, k_str2str } }; +/* + * Parse a pattern expression configuration: + * 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) +{ + const char *endw; + const char *end; + struct pattern_expr *expr; + struct pattern_fetch *fetch; + struct pattern_conv *conv; + unsigned long prev_type; + + if (!str[*idx]) + goto out_error; + + end = str[*idx] + strlen(str[*idx]); + endw = strchr(str[*idx], '('); + + if (!endw) + endw = end; + else if ((end-1)[0] != ')') + goto out_error; + + fetch = find_pattern_fetch(str[*idx], endw - str[*idx]); + if (!fetch) + goto out_error; + + if (fetch->out_type >= PATTERN_TYPES) + goto out_error; + + prev_type = fetch->out_type; + expr = calloc(1, sizeof(struct pattern_expr)); + + LIST_INIT(&(expr->conv_exprs)); + expr->fetch = fetch; + + if (end != endw) { + expr->arg_len = end - endw - 2; + expr->arg = malloc(expr->arg_len + 1); + expr->arg = memcpy(expr->arg, endw + 1, expr->arg_len); + expr->arg[expr->arg_len] = '\0'; + } + + for (*idx += 1; *(str[*idx]); (*idx)++) { + struct pattern_conv_expr *conv_expr; + + end = str[*idx] + strlen(str[*idx]); + endw = strchr(str[*idx], '('); + + if (!endw) + endw = end; + else if ((end-1)[0] != ')') + 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) + goto out_error; + + /* If impossible type conversion */ + if (!pattern_casts[prev_type][conv->in_type]) + goto out_error; + + prev_type = conv->out_type; + conv_expr = calloc(1, sizeof(struct pattern_conv_expr)); + + LIST_ADDQ(&(expr->conv_exprs), &(conv_expr->list)); + conv_expr->conv = conv; + + if (end != endw) { + conv_expr->arg_len = end - endw - 2; + conv_expr->arg = malloc(conv_expr->arg_len + 1); + conv_expr->arg = memcpy(conv_expr->arg, endw + 1, conv_expr->arg_len); + conv_expr->arg[expr->arg_len] = '\0'; + } + } + return expr; + +out_error: + /* TODO: prune_pattern_expr(expr); */ + return NULL; +} + +/* + * Process a fetch + format conversion of defined by the pattern expression + * on request or response considering the

parameter. + * Returns a pointer on a typed pattern structure containing the result or NULL if + * pattern is not found or when format conversion failed. + * If

is not null, function returns results in structure pointed by

. + * If

is null, functions returns a pointer on a static pattern structure. + */ +struct pattern *pattern_process(struct proxy *px, struct session *l4, void *l7, int dir, + struct pattern_expr *expr, struct pattern *p) +{ + struct pattern_conv_expr *conv_expr; + + if (p == NULL) + p = &spattern; + + if (!expr->fetch->process(px, l4, l7, dir, expr->arg, expr->arg_len, &p->data)) + return NULL; + + p->type = expr->fetch->out_type; + + list_for_each_entry(conv_expr, &expr->conv_exprs, list) { + if (!pattern_casts[p->type][conv_expr->conv->in_type](&p->data)) + return NULL; + + p->type = conv_expr->conv->in_type; + if (!conv_expr->conv->process(conv_expr->arg, expr->arg_len, &p->data)) + return NULL; + + p->type = conv_expr->conv->out_type; + } + return p; +} + +/* + * Process a fetch + format conversion of defined by the pattern expression + * on request or response considering the

parameter. + * Returns a pointer on a static tablekey structure of type of + * the converted result. + */ +struct stktable_key *pattern_process_key(struct proxy *px, struct session *l4, void *l7, int dir, + struct pattern_expr *expr, unsigned long table_type) +{ + struct pattern *ptrn; + + ptrn = pattern_process(px, l4, l7, dir, expr, NULL); + if (!ptrn) + return NULL; + + stable_key.key_len = (size_t)-1; + stable_key.key = pattern_keys[ptrn->type][table_type](&ptrn->data, &stable_key.data, &stable_key.key_len); + + if (!stable_key.key) + return NULL; + + return &stable_key; +} + +/* + * Returns 1 if pattern expression result cannot be converted to table key of + * type . + * + * Used in configuration check + */ +int pattern_notusable_key(struct pattern_expr *expr, unsigned long table_type) +{ + + if (table_type >= STKTABLE_TYPES) + return 1; + + if (LIST_ISEMPTY(&expr->conv_exprs)) { + if (!pattern_keys[expr->fetch->out_type][table_type]) + return 1; + } else { + struct pattern_conv_expr *conv_expr; + conv_expr = LIST_PREV(&expr->conv_exprs, typeof(conv_expr), list); + + if (!pattern_keys[conv_expr->conv->out_type][table_type]) + return 1; + } + return 0; +} + +/*****************************************************************/ +/* Pattern format convert functions */ +/*****************************************************************/ + +static int pattern_conv_str2lower(const char *arg, int arg_len, union pattern_data *data) +{ + int i; + + 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'; + } + return 1; +} + +static int pattern_conv_str2upper(const char *arg, int arg_len, union pattern_data *data) +{ + int i; + + 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'; + } + return 1; +} + +/* Note: must not be declared 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 }, + { NULL, NULL, 0, 0 }, +}}; + +__attribute__((constructor)) +static void __pattern_init(void) +{ + /* register pattern format convert keywords */ + pattern_register_convs(&pattern_conv_kws); +}