/* * 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, conv_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); }