blob: 6a12442fc3625d43674277d1024fe95de982e219 [file] [log] [blame]
Emeric Brun107ca302010-01-04 16:16:05 +01001/*
2 * Patterns management functions.
3 *
4 * Copyright 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <string.h>
14#include <arpa/inet.h>
Emeric Brun107ca302010-01-04 16:16:05 +010015
16#include <proto/pattern.h>
17#include <common/standard.h>
18
19/* static structure used on pattern_process if <p> is NULL*/
20static struct pattern spattern;
21
22/* trash chunk used for pattern conversions */
23static struct chunk trash_chunk;
24
25/* trash buffers used or pattern conversions */
26static char pattern_trash_buf1[BUFSIZE];
27static char pattern_trash_buf2[BUFSIZE];
28
29/* pattern_trash_buf point on used buffer*/
30static char *pattern_trash_buf = pattern_trash_buf1;
31
Emeric Brun107ca302010-01-04 16:16:05 +010032/* list head of all known pattern fetch keywords */
33static struct pattern_fetch_kw_list pattern_fetches = {
34 .list = LIST_HEAD_INIT(pattern_fetches.list)
35};
36
37/* list head of all known pattern format conversion keywords */
38static struct pattern_conv_kw_list pattern_convs = {
39 .list = LIST_HEAD_INIT(pattern_convs.list)
40};
41
42/*
43 * Registers the pattern fetch keyword list <kwl> as a list of valid keywords for next
44 * parsing sessions.
45 */
46void pattern_register_fetches(struct pattern_fetch_kw_list *pfkl)
47{
48 LIST_ADDQ(&pattern_fetches.list, &pfkl->list);
49}
50
51/*
52 * Registers the pattern format coverstion keyword list <pckl> as a list of valid keywords for next
53 * parsing sessions.
54 */
55void pattern_register_convs(struct pattern_conv_kw_list *pckl)
56{
57 LIST_ADDQ(&pattern_convs.list, &pckl->list);
58}
59
60/*
61 * Returns the pointer on pattern fetch keyword structure identified by
62 * string of <len> in buffer <kw>.
63 *
64 */
65struct pattern_fetch *find_pattern_fetch(const char *kw, int len)
66{
67 int index;
68 struct pattern_fetch_kw_list *kwl;
69
70 list_for_each_entry(kwl, &pattern_fetches.list, list) {
71 for (index = 0; kwl->kw[index].kw != NULL; index++) {
72 if (strncmp(kwl->kw[index].kw, kw, len) == 0 &&
73 kwl->kw[index].kw[len] == '\0')
74 return &kwl->kw[index];
75 }
76 }
77 return NULL;
78}
79
80/*
81 * Returns the pointer on pattern format conversion keyword structure identified by
82 * string of <len> in buffer <kw>.
83 *
84 */
85struct pattern_conv *find_pattern_conv(const char *kw, int len)
86{
87 int index;
88 struct pattern_conv_kw_list *kwl;
89
90 list_for_each_entry(kwl, &pattern_convs.list, list) {
91 for (index = 0; kwl->kw[index].kw != NULL; index++) {
92 if (strncmp(kwl->kw[index].kw, kw, len) == 0 &&
93 kwl->kw[index].kw[len] == '\0')
94 return &kwl->kw[index];
95 }
96 }
97 return NULL;
98}
99
100
101/*
102* Returns a static trash struct chunk to use in pattern casts or format conversions
103* Swiths the 2 available trash buffers to protect data during convert
104*/
105static struct chunk *get_trash_chunk(void)
106{
107 if (pattern_trash_buf == pattern_trash_buf1)
108 pattern_trash_buf = pattern_trash_buf2;
109 else
110 pattern_trash_buf = pattern_trash_buf1;
111
112 trash_chunk.str = pattern_trash_buf;
113 trash_chunk.len = 0;
114 trash_chunk.size = BUFSIZE;
115
116 return &trash_chunk;
117}
118
119/*
120* Used to set pattern data from a struct chunk, could be the trash struct chunk
121*/
122static void pattern_data_setstring(union pattern_data *data, struct chunk *c)
123{
124 data->str.str = c->str;
125 data->str.len = c->len;
126 data->str.size = c->size;
127}
128
129/******************************************************************/
130/* Pattern casts functions */
131/******************************************************************/
132
133static int c_ip2int(union pattern_data *data)
134{
135 data->integer = ntohl(data->ip.s_addr);
136 return 1;
137}
138
139static int c_ip2str(union pattern_data *data)
140{
141 struct chunk *trash = get_trash_chunk();
142
143 if (!inet_ntop(AF_INET, (void *)&data->ip, trash->str, trash->size))
144 return 0;
145
146 trash->len = strlen(trash->str);
147 pattern_data_setstring(data, trash);
148
149 return 1;
150}
151
152static int c_int2ip(union pattern_data *data)
153{
154 data->ip.s_addr = htonl(data->integer);
155 return 1;
156}
157
Emeric Brun107ca302010-01-04 16:16:05 +0100158static int c_str2ip(union pattern_data *data)
159{
160 if (!buf2ip(data->str.str, data->str.len, &data->ip))
161 return 0;
162 return 1;
163}
164
165static int c_int2str(union pattern_data *data)
166{
167 struct chunk *trash = get_trash_chunk();
168 char *pos;
169
170 pos = ultoa_r(data->integer, trash->str, trash->size);
171
172 if (!pos)
173 return 0;
174
175 trash->str = pos;
176 trash->len = strlen(pos);
177
178 pattern_data_setstring(data, trash);
179
180 return 1;
181}
182
183static int c_donothing(union pattern_data *data)
184{
185 return 1;
186}
187
188static int c_str2int(union pattern_data *data)
189{
190 int i;
191 uint32_t ret = 0;
192
193 for (i = 0; i < data->str.len; i++) {
194 uint32_t val = data->str.str[i] - '0';
195
196 if (val > 9)
197 break;
198
199 ret = ret * 10 + val;
200 }
201
202 data->integer = ret;
203 return 1;
204}
205
206/*****************************************************************/
207/* Pattern casts matrix: */
208/* pattern_casts[from type][to type] */
209/* NULL pointer used for impossible pattern casts */
210/*****************************************************************/
Emeric Brun107ca302010-01-04 16:16:05 +0100211
Willy Tarreauf0b38bf2010-06-06 13:22:23 +0200212typedef int (*pattern_cast_fct)(union pattern_data *data);
213static pattern_cast_fct pattern_casts[PATTERN_TYPES][PATTERN_TYPES] = {
214 { c_donothing, c_ip2int, c_ip2str },
215 { c_int2ip, c_donothing, c_int2str },
216 { c_str2ip, c_str2int, c_donothing },
217};
Emeric Brun107ca302010-01-04 16:16:05 +0100218
Emeric Brun107ca302010-01-04 16:16:05 +0100219
Emeric Brun107ca302010-01-04 16:16:05 +0100220/*
221 * Parse a pattern expression configuration:
222 * fetch keyword followed by format conversion keywords.
223 * Returns a pointer on allocated pattern expression structure.
224 */
225struct pattern_expr *pattern_parse_expr(char **str, int *idx)
226{
227 const char *endw;
228 const char *end;
229 struct pattern_expr *expr;
230 struct pattern_fetch *fetch;
231 struct pattern_conv *conv;
232 unsigned long prev_type;
233
234 if (!str[*idx])
235 goto out_error;
236
237 end = str[*idx] + strlen(str[*idx]);
238 endw = strchr(str[*idx], '(');
239
240 if (!endw)
241 endw = end;
242 else if ((end-1)[0] != ')')
243 goto out_error;
244
245 fetch = find_pattern_fetch(str[*idx], endw - str[*idx]);
246 if (!fetch)
247 goto out_error;
248
249 if (fetch->out_type >= PATTERN_TYPES)
250 goto out_error;
251
252 prev_type = fetch->out_type;
253 expr = calloc(1, sizeof(struct pattern_expr));
254
255 LIST_INIT(&(expr->conv_exprs));
256 expr->fetch = fetch;
257
258 if (end != endw) {
259 expr->arg_len = end - endw - 2;
Willy Tarreauac778f52010-01-26 19:02:46 +0100260 expr->arg = my_strndup(endw + 1, expr->arg_len);
Emeric Brun107ca302010-01-04 16:16:05 +0100261 }
262
263 for (*idx += 1; *(str[*idx]); (*idx)++) {
264 struct pattern_conv_expr *conv_expr;
265
266 end = str[*idx] + strlen(str[*idx]);
267 endw = strchr(str[*idx], '(');
268
269 if (!endw)
270 endw = end;
271 else if ((end-1)[0] != ')')
272 goto out_error;
273
274 conv = find_pattern_conv(str[*idx], endw - str[*idx]);
275 if (!conv)
276 break;
277
278 if (conv->in_type >= PATTERN_TYPES ||
279 conv->out_type >= PATTERN_TYPES)
280 goto out_error;
281
282 /* If impossible type conversion */
283 if (!pattern_casts[prev_type][conv->in_type])
284 goto out_error;
285
286 prev_type = conv->out_type;
287 conv_expr = calloc(1, sizeof(struct pattern_conv_expr));
288
289 LIST_ADDQ(&(expr->conv_exprs), &(conv_expr->list));
290 conv_expr->conv = conv;
291
292 if (end != endw) {
Willy Tarreau9e92d322010-01-26 17:58:06 +0100293 int i = end - endw - 2;
294 char *p = my_strndup(endw + 1, i);
295
296 if (conv->parse_args) {
297 i = conv->parse_args(p, &conv_expr->arg_p, &conv_expr->arg_i);
298 free(p);
299 if (!i)
300 goto out_error;
301 } else {
302 conv_expr->arg_i = i;
303 conv_expr->arg_p = p;
304 }
Emeric Brun107ca302010-01-04 16:16:05 +0100305 }
306 }
307 return expr;
308
309out_error:
310 /* TODO: prune_pattern_expr(expr); */
311 return NULL;
312}
313
314/*
315 * Process a fetch + format conversion of defined by the pattern expression <expr>
316 * on request or response considering the <dir> parameter.
317 * Returns a pointer on a typed pattern structure containing the result or NULL if
318 * pattern is not found or when format conversion failed.
319 * If <p> is not null, function returns results in structure pointed by <p>.
320 * If <p> is null, functions returns a pointer on a static pattern structure.
321 */
322struct pattern *pattern_process(struct proxy *px, struct session *l4, void *l7, int dir,
323 struct pattern_expr *expr, struct pattern *p)
324{
325 struct pattern_conv_expr *conv_expr;
326
327 if (p == NULL)
328 p = &spattern;
329
330 if (!expr->fetch->process(px, l4, l7, dir, expr->arg, expr->arg_len, &p->data))
331 return NULL;
332
333 p->type = expr->fetch->out_type;
334
335 list_for_each_entry(conv_expr, &expr->conv_exprs, list) {
336 if (!pattern_casts[p->type][conv_expr->conv->in_type](&p->data))
337 return NULL;
338
339 p->type = conv_expr->conv->in_type;
Willy Tarreau1a51b632010-01-26 17:17:56 +0100340 if (!conv_expr->conv->process(conv_expr->arg_p, conv_expr->arg_i, &p->data))
Emeric Brun107ca302010-01-04 16:16:05 +0100341 return NULL;
342
343 p->type = conv_expr->conv->out_type;
344 }
345 return p;
346}
347
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100348/* Converts an argument string to an IPv4 mask stored in network byte order in
349 * arg_i. Returns non-zero in case of success, 0 on error.
350 */
351static int pattern_conv_arg_to_ipmask(const char *arg_str, void **arg_p, int *arg_i)
352{
353 struct in_addr mask;
354
355 if (!str2mask(arg_str, &mask))
356 return 0;
357
358 *arg_i = mask.s_addr;
359 return 1;
360}
361
Emeric Brun107ca302010-01-04 16:16:05 +0100362/*****************************************************************/
363/* Pattern format convert functions */
364/*****************************************************************/
365
Willy Tarreau1a51b632010-01-26 17:17:56 +0100366static int pattern_conv_str2lower(const void *arg_p, int arg_i, union pattern_data *data)
Emeric Brun107ca302010-01-04 16:16:05 +0100367{
368 int i;
369
370 for (i = 0; i < data->str.len; i++) {
371 if ((data->str.str[i] >= 'A') && (data->str.str[i] <= 'Z'))
372 data->str.str[i] += 'a' - 'A';
373 }
374 return 1;
375}
376
Willy Tarreau1a51b632010-01-26 17:17:56 +0100377static int pattern_conv_str2upper(const void *arg_p, int arg_i, union pattern_data *data)
Emeric Brun107ca302010-01-04 16:16:05 +0100378{
379 int i;
380
381 for (i = 0; i < data->str.len; i++) {
382 if ((data->str.str[i] >= 'a') && (data->str.str[i] <= 'z'))
383 data->str.str[i] += 'A' - 'a';
384 }
385 return 1;
386}
387
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100388/* takes the netmask in arg_i */
389static int pattern_conv_ipmask(const void *arg_p, int arg_i, union pattern_data *data)
390{
391 data->ip.s_addr &= arg_i;
392 return 1;
393}
394
Emeric Brun107ca302010-01-04 16:16:05 +0100395/* Note: must not be declared <const> as its list will be overwritten */
396static struct pattern_conv_kw_list pattern_conv_kws = {{ },{
397 { "upper", pattern_conv_str2upper, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
398 { "lower", pattern_conv_str2lower, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100399 { "ipmask", pattern_conv_ipmask, PATTERN_TYPE_IP, PATTERN_TYPE_IP, pattern_conv_arg_to_ipmask },
Emeric Brun107ca302010-01-04 16:16:05 +0100400 { NULL, NULL, 0, 0 },
401}};
402
403__attribute__((constructor))
404static void __pattern_init(void)
405{
406 /* register pattern format convert keywords */
407 pattern_register_convs(&pattern_conv_kws);
408}