| /* |
| * Patterns management functions. |
| * |
| * Copyright 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr> |
| * |
| * 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 <string.h> |
| #include <arpa/inet.h> |
| #include <types/stick_table.h> |
| |
| #include <proto/pattern.h> |
| #include <common/standard.h> |
| |
| /* static structure used on pattern_process if <p> 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 <kwl> 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 <pckl> 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 <len> in buffer <kw>. |
| * |
| */ |
| 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 <len> in buffer <kw>. |
| * |
| */ |
| 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 = my_strndup(endw + 1, expr->arg_len); |
| } |
| |
| 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) { |
| int i = end - endw - 2; |
| char *p = my_strndup(endw + 1, i); |
| |
| if (conv->parse_args) { |
| i = conv->parse_args(p, &conv_expr->arg_p, &conv_expr->arg_i); |
| free(p); |
| if (!i) |
| goto out_error; |
| } else { |
| conv_expr->arg_i = i; |
| conv_expr->arg_p = p; |
| } |
| } |
| } |
| return expr; |
| |
| out_error: |
| /* TODO: prune_pattern_expr(expr); */ |
| return NULL; |
| } |
| |
| /* |
| * Process a fetch + format conversion of defined by the pattern expression <expr> |
| * on request or response considering the <dir> 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 <p> is not null, function returns results in structure pointed by <p>. |
| * If <p> 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_p, conv_expr->arg_i, &p->data)) |
| return NULL; |
| |
| p->type = conv_expr->conv->out_type; |
| } |
| return p; |
| } |
| |
| /* |
| * Process a fetch + format conversion of defined by the pattern expression <expr> |
| * on request or response considering the <dir> parameter. |
| * Returns a pointer on a static tablekey structure of type <table_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 <expr> result cannot be converted to table key of |
| * type <table_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; |
| } |
| |
| /* 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. |
| */ |
| static int pattern_conv_arg_to_ipmask(const char *arg_str, void **arg_p, int *arg_i) |
| { |
| struct in_addr mask; |
| |
| if (!str2mask(arg_str, &mask)) |
| return 0; |
| |
| *arg_i = mask.s_addr; |
| return 1; |
| } |
| |
| /*****************************************************************/ |
| /* Pattern format convert functions */ |
| /*****************************************************************/ |
| |
| static int pattern_conv_str2lower(const void *arg_p, int arg_i, 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 void *arg_p, int arg_i, 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; |
| } |
| |
| /* takes the netmask in arg_i */ |
| static int pattern_conv_ipmask(const void *arg_p, int arg_i, union pattern_data *data) |
| { |
| data->ip.s_addr &= arg_i; |
| return 1; |
| } |
| |
| /* 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 }, |
| }}; |
| |
| __attribute__((constructor)) |
| static void __pattern_init(void) |
| { |
| /* register pattern format convert keywords */ |
| pattern_register_convs(&pattern_conv_kws); |
| } |