blob: 78728f617a8c3196e6fbb798a65f753bf90cbf75 [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>
Emeric Brun485479d2010-09-23 18:02:19 +020017#include <proto/buffers.h>
Emeric Brun107ca302010-01-04 16:16:05 +010018#include <common/standard.h>
19
20/* static structure used on pattern_process if <p> is NULL*/
Willy Tarreau5e6cc4a2011-12-16 15:23:14 +010021struct pattern temp_pattern = { };
Emeric Brun107ca302010-01-04 16:16:05 +010022
23/* trash chunk used for pattern conversions */
24static struct chunk trash_chunk;
25
26/* trash buffers used or pattern conversions */
27static char pattern_trash_buf1[BUFSIZE];
28static char pattern_trash_buf2[BUFSIZE];
29
30/* pattern_trash_buf point on used buffer*/
31static char *pattern_trash_buf = pattern_trash_buf1;
32
Emeric Brun107ca302010-01-04 16:16:05 +010033/* list head of all known pattern fetch keywords */
34static struct pattern_fetch_kw_list pattern_fetches = {
35 .list = LIST_HEAD_INIT(pattern_fetches.list)
36};
37
38/* list head of all known pattern format conversion keywords */
39static struct pattern_conv_kw_list pattern_convs = {
40 .list = LIST_HEAD_INIT(pattern_convs.list)
41};
42
43/*
44 * Registers the pattern fetch keyword list <kwl> as a list of valid keywords for next
45 * parsing sessions.
46 */
47void pattern_register_fetches(struct pattern_fetch_kw_list *pfkl)
48{
49 LIST_ADDQ(&pattern_fetches.list, &pfkl->list);
50}
51
52/*
53 * Registers the pattern format coverstion keyword list <pckl> as a list of valid keywords for next
54 * parsing sessions.
55 */
56void pattern_register_convs(struct pattern_conv_kw_list *pckl)
57{
58 LIST_ADDQ(&pattern_convs.list, &pckl->list);
59}
60
61/*
62 * Returns the pointer on pattern fetch keyword structure identified by
63 * string of <len> in buffer <kw>.
64 *
65 */
66struct pattern_fetch *find_pattern_fetch(const char *kw, int len)
67{
68 int index;
69 struct pattern_fetch_kw_list *kwl;
70
71 list_for_each_entry(kwl, &pattern_fetches.list, list) {
72 for (index = 0; kwl->kw[index].kw != NULL; index++) {
73 if (strncmp(kwl->kw[index].kw, kw, len) == 0 &&
74 kwl->kw[index].kw[len] == '\0')
75 return &kwl->kw[index];
76 }
77 }
78 return NULL;
79}
80
81/*
82 * Returns the pointer on pattern format conversion keyword structure identified by
83 * string of <len> in buffer <kw>.
84 *
85 */
86struct pattern_conv *find_pattern_conv(const char *kw, int len)
87{
88 int index;
89 struct pattern_conv_kw_list *kwl;
90
91 list_for_each_entry(kwl, &pattern_convs.list, list) {
92 for (index = 0; kwl->kw[index].kw != NULL; index++) {
93 if (strncmp(kwl->kw[index].kw, kw, len) == 0 &&
94 kwl->kw[index].kw[len] == '\0')
95 return &kwl->kw[index];
96 }
97 }
98 return NULL;
99}
100
101
102/*
103* Returns a static trash struct chunk to use in pattern casts or format conversions
104* Swiths the 2 available trash buffers to protect data during convert
105*/
106static struct chunk *get_trash_chunk(void)
107{
108 if (pattern_trash_buf == pattern_trash_buf1)
109 pattern_trash_buf = pattern_trash_buf2;
110 else
111 pattern_trash_buf = pattern_trash_buf1;
112
Emeric Brun485479d2010-09-23 18:02:19 +0200113 chunk_init(&trash_chunk, pattern_trash_buf, BUFSIZE);
Emeric Brun107ca302010-01-04 16:16:05 +0100114
115 return &trash_chunk;
116}
117
Emeric Brun107ca302010-01-04 16:16:05 +0100118/******************************************************************/
119/* Pattern casts functions */
120/******************************************************************/
121
122static int c_ip2int(union pattern_data *data)
123{
124 data->integer = ntohl(data->ip.s_addr);
125 return 1;
126}
127
128static int c_ip2str(union pattern_data *data)
129{
130 struct chunk *trash = get_trash_chunk();
131
132 if (!inet_ntop(AF_INET, (void *)&data->ip, trash->str, trash->size))
133 return 0;
134
135 trash->len = strlen(trash->str);
Willy Tarreau1ded6052011-12-16 15:35:46 +0100136 data->str = *trash;
Emeric Brun107ca302010-01-04 16:16:05 +0100137
138 return 1;
139}
140
David du Colombier4f92d322011-03-24 11:09:31 +0100141static int c_ip2ipv6(union pattern_data *data)
142{
143 v4tov6(&data->ipv6, &data->ip);
144 return 1;
145}
146
147static int c_ipv62str(union pattern_data *data)
148{
149 struct chunk *trash = get_trash_chunk();
150
151 if (!inet_ntop(AF_INET6, (void *)&data->ipv6, trash->str, trash->size))
152 return 0;
153
154 trash->len = strlen(trash->str);
Willy Tarreau1ded6052011-12-16 15:35:46 +0100155 data->str = *trash;
David du Colombier4f92d322011-03-24 11:09:31 +0100156 return 1;
157}
158
159/*
160static int c_ipv62ip(union pattern_data *data)
161{
162 return v6tov4(&data->ip, &data->ipv6);
163}
164*/
165
Emeric Brun107ca302010-01-04 16:16:05 +0100166static int c_int2ip(union pattern_data *data)
167{
168 data->ip.s_addr = htonl(data->integer);
169 return 1;
170}
171
Emeric Brun107ca302010-01-04 16:16:05 +0100172static int c_str2ip(union pattern_data *data)
173{
174 if (!buf2ip(data->str.str, data->str.len, &data->ip))
175 return 0;
176 return 1;
177}
178
David du Colombier4f92d322011-03-24 11:09:31 +0100179static int c_str2ipv6(union pattern_data *data)
180{
181 return inet_pton(AF_INET6, data->str.str, &data->ipv6);
182}
183
Emeric Brun107ca302010-01-04 16:16:05 +0100184static int c_int2str(union pattern_data *data)
185{
186 struct chunk *trash = get_trash_chunk();
187 char *pos;
188
189 pos = ultoa_r(data->integer, trash->str, trash->size);
190
191 if (!pos)
192 return 0;
193
Emeric Brun485479d2010-09-23 18:02:19 +0200194 trash->size = trash->size - (pos - trash->str);
Emeric Brun107ca302010-01-04 16:16:05 +0100195 trash->str = pos;
196 trash->len = strlen(pos);
Willy Tarreau1ded6052011-12-16 15:35:46 +0100197 data->str = *trash;
Emeric Brun107ca302010-01-04 16:16:05 +0100198 return 1;
199}
200
Emeric Brun485479d2010-09-23 18:02:19 +0200201static int c_datadup(union pattern_data *data)
202{
203 struct chunk *trash = get_trash_chunk();
204
205 trash->len = data->str.len < trash->size ? data->str.len : trash->size;
206 memcpy(trash->str, data->str.str, trash->len);
Willy Tarreau1ded6052011-12-16 15:35:46 +0100207 data->str = *trash;
Emeric Brun485479d2010-09-23 18:02:19 +0200208 return 1;
209}
210
211
Emeric Brun107ca302010-01-04 16:16:05 +0100212static int c_donothing(union pattern_data *data)
213{
214 return 1;
215}
216
217static int c_str2int(union pattern_data *data)
218{
219 int i;
220 uint32_t ret = 0;
221
222 for (i = 0; i < data->str.len; i++) {
223 uint32_t val = data->str.str[i] - '0';
224
225 if (val > 9)
226 break;
227
228 ret = ret * 10 + val;
229 }
230
231 data->integer = ret;
232 return 1;
233}
234
235/*****************************************************************/
236/* Pattern casts matrix: */
237/* pattern_casts[from type][to type] */
238/* NULL pointer used for impossible pattern casts */
239/*****************************************************************/
Emeric Brun107ca302010-01-04 16:16:05 +0100240
Willy Tarreauf0b38bf2010-06-06 13:22:23 +0200241typedef int (*pattern_cast_fct)(union pattern_data *data);
242static pattern_cast_fct pattern_casts[PATTERN_TYPES][PATTERN_TYPES] = {
David du Colombier4f92d322011-03-24 11:09:31 +0100243/* to: IP IPV6 INTEGER STRING DATA CONSTSTRING CONSTDATA */
244/* from: IP */ { c_donothing, c_ip2ipv6, c_ip2int, c_ip2str, NULL, c_ip2str, NULL },
245/* IPV6 */ { NULL, c_donothing, NULL, c_ipv62str, NULL, c_ipv62str, NULL },
246/* INTEGER */ { c_int2ip, NULL, c_donothing, c_int2str, NULL, c_int2str, NULL },
247/* STRING */ { c_str2ip, c_str2ipv6, c_str2int, c_donothing, c_donothing, c_donothing, c_donothing },
248/* DATA */ { NULL, NULL, NULL, NULL, c_donothing, NULL, c_donothing },
249/* CONSTSTRING */ { c_str2ip, c_str2ipv6, c_str2int, c_datadup, c_datadup, c_donothing, c_donothing },
250/* CONSTDATA */ { NULL, NULL, NULL, NULL, c_datadup, NULL, c_donothing },
Willy Tarreauf0b38bf2010-06-06 13:22:23 +0200251};
Emeric Brun107ca302010-01-04 16:16:05 +0100252
Emeric Brun107ca302010-01-04 16:16:05 +0100253
Emeric Brun107ca302010-01-04 16:16:05 +0100254/*
255 * Parse a pattern expression configuration:
256 * fetch keyword followed by format conversion keywords.
257 * Returns a pointer on allocated pattern expression structure.
258 */
Emeric Brun485479d2010-09-23 18:02:19 +0200259struct pattern_expr *pattern_parse_expr(char **str, int *idx, char *err, int err_size)
Emeric Brun107ca302010-01-04 16:16:05 +0100260{
261 const char *endw;
262 const char *end;
263 struct pattern_expr *expr;
264 struct pattern_fetch *fetch;
265 struct pattern_conv *conv;
266 unsigned long prev_type;
Emeric Brun485479d2010-09-23 18:02:19 +0200267 char *p;
Emeric Brun107ca302010-01-04 16:16:05 +0100268
Emeric Brun485479d2010-09-23 18:02:19 +0200269 snprintf(err, err_size, "memory error.");
270 if (!str[*idx]) {
271
272 snprintf(err, err_size, "missing fetch method.");
Emeric Brun107ca302010-01-04 16:16:05 +0100273 goto out_error;
Emeric Brun485479d2010-09-23 18:02:19 +0200274 }
Emeric Brun107ca302010-01-04 16:16:05 +0100275
276 end = str[*idx] + strlen(str[*idx]);
277 endw = strchr(str[*idx], '(');
278
279 if (!endw)
280 endw = end;
Emeric Brun485479d2010-09-23 18:02:19 +0200281 else if ((end-1)[0] != ')') {
282 p = my_strndup(str[*idx], endw - str[*idx]);
283 if (p) {
284 snprintf(err, err_size, "syntax error: missing ')' after keyword '%s'.", p);
285 free(p);
286 }
Emeric Brun107ca302010-01-04 16:16:05 +0100287 goto out_error;
Emeric Brun485479d2010-09-23 18:02:19 +0200288 }
Emeric Brun107ca302010-01-04 16:16:05 +0100289
290 fetch = find_pattern_fetch(str[*idx], endw - str[*idx]);
Emeric Brun485479d2010-09-23 18:02:19 +0200291 if (!fetch) {
292 p = my_strndup(str[*idx], endw - str[*idx]);
293 if (p) {
294 snprintf(err, err_size, "unknown fetch method '%s'.", p);
295 free(p);
296 }
Emeric Brun107ca302010-01-04 16:16:05 +0100297 goto out_error;
Emeric Brun485479d2010-09-23 18:02:19 +0200298 }
299 if (fetch->out_type >= PATTERN_TYPES) {
Emeric Brun107ca302010-01-04 16:16:05 +0100300
Emeric Brun485479d2010-09-23 18:02:19 +0200301 p = my_strndup(str[*idx], endw - str[*idx]);
302 if (p) {
303 snprintf(err, err_size, "returns type of fetch method '%s' is unknown.", p);
304 free(p);
305 }
Emeric Brun107ca302010-01-04 16:16:05 +0100306 goto out_error;
Emeric Brun485479d2010-09-23 18:02:19 +0200307 }
Emeric Brun107ca302010-01-04 16:16:05 +0100308
309 prev_type = fetch->out_type;
310 expr = calloc(1, sizeof(struct pattern_expr));
Emeric Brun485479d2010-09-23 18:02:19 +0200311 if (!expr)
312 goto out_error;
Emeric Brun107ca302010-01-04 16:16:05 +0100313
314 LIST_INIT(&(expr->conv_exprs));
315 expr->fetch = fetch;
316
317 if (end != endw) {
Emeric Brun485479d2010-09-23 18:02:19 +0200318 int i = end - endw - 2;
319
320 if (!fetch->parse_args) {
321 p = my_strndup(str[*idx], endw - str[*idx]);
322 if (p) {
323 snprintf(err, err_size, "fetch method '%s' does not support any args.", p);
324 free(p);
325 }
326 goto out_error;
327 }
328 p = my_strndup(endw + 1, i);
329 if (!p)
330 goto out_error;
331 i = fetch->parse_args(p, &expr->arg_p, &expr->arg_i);
332 free(p);
333 if (!i) {
334 p = my_strndup(str[*idx], endw - str[*idx]);
335 if (p) {
336 snprintf(err, err_size, "invalid args in fetch method '%s'.", p);
337 free(p);
338 }
339 goto out_error;
340 }
341 }
342 else if (fetch->parse_args) {
343 p = my_strndup(str[*idx], endw - str[*idx]);
344 if (p) {
345 snprintf(err, err_size, "missing args for fetch method '%s'.", p);
346 free(p);
347 }
348 goto out_error;
Emeric Brun107ca302010-01-04 16:16:05 +0100349 }
350
351 for (*idx += 1; *(str[*idx]); (*idx)++) {
352 struct pattern_conv_expr *conv_expr;
353
354 end = str[*idx] + strlen(str[*idx]);
355 endw = strchr(str[*idx], '(');
356
357 if (!endw)
358 endw = end;
Emeric Brun485479d2010-09-23 18:02:19 +0200359 else if ((end-1)[0] != ')') {
360 p = my_strndup(str[*idx], endw - str[*idx]);
361 if (p) {
362 snprintf(err, err_size, "syntax error, missing ')' after keyword '%s'.", p);
363 free(p);
364 }
Emeric Brun107ca302010-01-04 16:16:05 +0100365 goto out_error;
Emeric Brun485479d2010-09-23 18:02:19 +0200366 }
Emeric Brun107ca302010-01-04 16:16:05 +0100367
368 conv = find_pattern_conv(str[*idx], endw - str[*idx]);
369 if (!conv)
370 break;
371
372 if (conv->in_type >= PATTERN_TYPES ||
Emeric Brun485479d2010-09-23 18:02:19 +0200373 conv->out_type >= PATTERN_TYPES) {
374 p = my_strndup(str[*idx], endw - str[*idx]);
375 if (p) {
376 snprintf(err, err_size, "returns type of conv method '%s' is unknown.", p);
377 free(p);
378 }
Emeric Brun107ca302010-01-04 16:16:05 +0100379 goto out_error;
Emeric Brun485479d2010-09-23 18:02:19 +0200380 }
Emeric Brun107ca302010-01-04 16:16:05 +0100381
382 /* If impossible type conversion */
Emeric Brun485479d2010-09-23 18:02:19 +0200383 if (!pattern_casts[prev_type][conv->in_type]) {
384 p = my_strndup(str[*idx], endw - str[*idx]);
385 if (p) {
386 snprintf(err, err_size, "conv method '%s' cannot be applied.", p);
387 free(p);
388 }
Emeric Brun107ca302010-01-04 16:16:05 +0100389 goto out_error;
Emeric Brun485479d2010-09-23 18:02:19 +0200390 }
Emeric Brun107ca302010-01-04 16:16:05 +0100391
392 prev_type = conv->out_type;
393 conv_expr = calloc(1, sizeof(struct pattern_conv_expr));
Emeric Brun485479d2010-09-23 18:02:19 +0200394 if (!conv_expr)
395 goto out_error;
Emeric Brun107ca302010-01-04 16:16:05 +0100396
397 LIST_ADDQ(&(expr->conv_exprs), &(conv_expr->list));
398 conv_expr->conv = conv;
399
400 if (end != endw) {
Willy Tarreau9e92d322010-01-26 17:58:06 +0100401 int i = end - endw - 2;
Emeric Brun485479d2010-09-23 18:02:19 +0200402
403 if (!conv->parse_args) {
404 p = my_strndup(str[*idx], endw - str[*idx]);
405
406 if (p) {
407 snprintf(err, err_size, "conv method '%s' does not support any args.", p);
408 free(p);
409 }
410 goto out_error;
411 }
Willy Tarreau9e92d322010-01-26 17:58:06 +0100412
Emeric Brun485479d2010-09-23 18:02:19 +0200413 p = my_strndup(endw + 1, i);
414 if (!p)
415 goto out_error;
416 i = conv->parse_args(p, &conv_expr->arg_p, &conv_expr->arg_i);
417 free(p);
418 if (!i) {
419 p = my_strndup(str[*idx], endw - str[*idx]);
420 if (p) {
421 snprintf(err, err_size, "invalid args in conv method '%s'.", p);
422 free(p);
423 }
424 goto out_error;
425 }
426 }
427 else if (conv->parse_args) {
428 p = my_strndup(str[*idx], endw - str[*idx]);
429 if (p) {
430 snprintf(err, err_size, "missing args for conv method '%s'.", p);
Willy Tarreau9e92d322010-01-26 17:58:06 +0100431 free(p);
Willy Tarreau9e92d322010-01-26 17:58:06 +0100432 }
Emeric Brun485479d2010-09-23 18:02:19 +0200433 goto out_error;
Emeric Brun107ca302010-01-04 16:16:05 +0100434 }
Emeric Brun485479d2010-09-23 18:02:19 +0200435
Emeric Brun107ca302010-01-04 16:16:05 +0100436 }
Emeric Brun485479d2010-09-23 18:02:19 +0200437
Emeric Brun107ca302010-01-04 16:16:05 +0100438 return expr;
439
440out_error:
441 /* TODO: prune_pattern_expr(expr); */
442 return NULL;
443}
444
445/*
446 * Process a fetch + format conversion of defined by the pattern expression <expr>
447 * on request or response considering the <dir> parameter.
448 * Returns a pointer on a typed pattern structure containing the result or NULL if
449 * pattern is not found or when format conversion failed.
450 * If <p> is not null, function returns results in structure pointed by <p>.
451 * If <p> is null, functions returns a pointer on a static pattern structure.
452 */
453struct pattern *pattern_process(struct proxy *px, struct session *l4, void *l7, int dir,
454 struct pattern_expr *expr, struct pattern *p)
455{
456 struct pattern_conv_expr *conv_expr;
457
458 if (p == NULL)
Willy Tarreau5e6cc4a2011-12-16 15:23:14 +0100459 p = &temp_pattern;
Emeric Brun107ca302010-01-04 16:16:05 +0100460
Emeric Brun485479d2010-09-23 18:02:19 +0200461 if (!expr->fetch->process(px, l4, l7, dir, expr->arg_p, expr->arg_i, &p->data))
Emeric Brun107ca302010-01-04 16:16:05 +0100462 return NULL;
463
464 p->type = expr->fetch->out_type;
465
466 list_for_each_entry(conv_expr, &expr->conv_exprs, list) {
467 if (!pattern_casts[p->type][conv_expr->conv->in_type](&p->data))
468 return NULL;
469
470 p->type = conv_expr->conv->in_type;
Willy Tarreau1a51b632010-01-26 17:17:56 +0100471 if (!conv_expr->conv->process(conv_expr->arg_p, conv_expr->arg_i, &p->data))
Emeric Brun107ca302010-01-04 16:16:05 +0100472 return NULL;
473
474 p->type = conv_expr->conv->out_type;
475 }
476 return p;
477}
478
Emeric Brun485479d2010-09-23 18:02:19 +0200479/* Converts an argument string mask to a pattern_arg type IP.
480 * Returns non-zero in case of success, 0 on error.
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100481 */
Emeric Brun485479d2010-09-23 18:02:19 +0200482int pattern_arg_ipmask(const char *arg_str, struct pattern_arg **arg_p, int *arg_i)
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100483{
Emeric Brun485479d2010-09-23 18:02:19 +0200484 *arg_i = 1;
485 *arg_p = calloc(1, *arg_i*sizeof(struct pattern_arg));
486 (*arg_p)->type = PATTERN_ARG_TYPE_IP;
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100487
Emeric Brun485479d2010-09-23 18:02:19 +0200488 if (!str2mask(arg_str, &(*arg_p)->data.ip))
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100489 return 0;
490
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100491 return 1;
492}
493
Emeric Brun485479d2010-09-23 18:02:19 +0200494
495/* Converts an argument string to a pattern_arg type STRING.
496 * Returns non-zero in case of success, 0 on error.
497 */
498int pattern_arg_str(const char *arg_str, struct pattern_arg **arg_p, int *arg_i)
499{
500 *arg_i = 1;
501 *arg_p = calloc(1, *arg_i*sizeof(struct pattern_arg));
502 (*arg_p)->type = PATTERN_ARG_TYPE_STRING;
503 (*arg_p)->data.str.str = strdup(arg_str);
504 (*arg_p)->data.str.len = strlen(arg_str);
505
506
507 return 1;
508}
509
510
Emeric Brun107ca302010-01-04 16:16:05 +0100511/*****************************************************************/
512/* Pattern format convert functions */
513/*****************************************************************/
514
Emeric Brun485479d2010-09-23 18:02:19 +0200515static int pattern_conv_str2lower(const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
Emeric Brun107ca302010-01-04 16:16:05 +0100516{
517 int i;
518
Emeric Brun485479d2010-09-23 18:02:19 +0200519 if (!data->str.size)
520 return 0;
521
Emeric Brun107ca302010-01-04 16:16:05 +0100522 for (i = 0; i < data->str.len; i++) {
523 if ((data->str.str[i] >= 'A') && (data->str.str[i] <= 'Z'))
524 data->str.str[i] += 'a' - 'A';
525 }
526 return 1;
527}
528
Emeric Brun485479d2010-09-23 18:02:19 +0200529static int pattern_conv_str2upper(const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
Emeric Brun107ca302010-01-04 16:16:05 +0100530{
531 int i;
532
Emeric Brun485479d2010-09-23 18:02:19 +0200533 if (!data->str.size)
534 return 0;
535
Emeric Brun107ca302010-01-04 16:16:05 +0100536 for (i = 0; i < data->str.len; i++) {
537 if ((data->str.str[i] >= 'a') && (data->str.str[i] <= 'z'))
538 data->str.str[i] += 'A' - 'a';
539 }
540 return 1;
541}
542
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100543/* takes the netmask in arg_i */
Emeric Brun485479d2010-09-23 18:02:19 +0200544static int pattern_conv_ipmask(const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100545{
Simon Hormand281eed2011-08-13 08:03:49 +0900546 data->ip.s_addr &= arg_p->data.ip.s_addr;
Willy Tarreaud31d6eb2010-01-26 18:01:41 +0100547 return 1;
548}
549
Emeric Brun107ca302010-01-04 16:16:05 +0100550/* Note: must not be declared <const> as its list will be overwritten */
551static struct pattern_conv_kw_list pattern_conv_kws = {{ },{
Emeric Brun485479d2010-09-23 18:02:19 +0200552 { "upper", pattern_conv_str2upper, NULL, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
553 { "lower", pattern_conv_str2lower, NULL, PATTERN_TYPE_STRING, PATTERN_TYPE_STRING },
554 { "ipmask", pattern_conv_ipmask, pattern_arg_ipmask, PATTERN_TYPE_IP, PATTERN_TYPE_IP },
555 { NULL, NULL, NULL, 0, 0 },
Emeric Brun107ca302010-01-04 16:16:05 +0100556}};
557
558__attribute__((constructor))
559static void __pattern_init(void)
560{
561 /* register pattern format convert keywords */
562 pattern_register_convs(&pattern_conv_kws);
563}