blob: b1fa7f672d336a46aec5b1dcf7eaf795a5e1b2bb [file] [log] [blame]
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +01001/*
2 * MAP management functions.
3 *
4 * Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
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 <limits.h>
14#include <stdio.h>
15
16#include <common/standard.h>
17
18#include <types/global.h>
19#include <types/map.h>
20
21#include <proto/arg.h>
22#include <proto/pattern.h>
23#include <proto/sample.h>
24
Thierry FOURNIER275db692013-12-06 10:39:36 +010025struct list maps = LIST_HEAD_INIT(maps); /* list of struct map_reference */
26
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +010027/* This function return existing map reference or return NULL. */
Thierry FOURNIER2d4771b2013-12-06 10:39:48 +010028struct map_reference *map_get_reference(const char *reference)
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +010029{
30 struct map_reference *ref;
31
32 /* process the lookup */
33 list_for_each_entry(ref, &maps, list)
34 if (strcmp(ref->reference, reference) == 0)
35 return ref;
36 return NULL;
37}
38
39/* Parse an IPv4 address and store it into the sample.
40 * The output type is IPV4.
41 */
42static int map_parse_ip(const char *text, struct sample_storage *smp)
43{
44 if (!buf2ip(text, strlen(text), &smp->data.ipv4))
45 return 0;
46 smp->type = SMP_T_IPV4;
47 return 1;
48}
49
50/* Parse an IPv6 address and store it into the sample.
51 * The output type is IPV6.
52 */
53static int map_parse_ip6(const char *text, struct sample_storage *smp)
54{
55 if (!buf2ip6(text, strlen(text), &smp->data.ipv6))
56 return 0;
57 smp->type = SMP_T_IPV6;
58 return 1;
59}
60
61/* Parse a string and store a pointer to it into the sample. The original
62 * string must be left in memory because we return a direct memory reference.
63 * The output type is CSTR.
64 */
65static int map_parse_str(const char *text, struct sample_storage *smp)
66{
67 /* The loose of the "const" is balanced by the SMP_T_CSTR type */
68 smp->data.str.str = (char *)text;
69 smp->data.str.len = strlen(text);
70 smp->data.str.size = smp->data.str.len + 1;
71 smp->type = SMP_T_CSTR;
72 return 1;
73}
74
75/* Parse an integer and convert it to a sample. The output type is SINT if the
76 * number is negative, or UINT if it is positive or null. The function returns
77 * zero (error) if the number is too large.
78 */
79static int map_parse_int(const char *text, struct sample_storage *smp)
80{
81 long long int value;
82 char *error;
83
84 /* parse interger and convert it. Return the value in 64 format. */
85 value = strtoll(text, &error, 10);
86 if (*error != '\0')
87 return 0;
88
89 /* check sign iand limits */
90 if (value < 0) {
91 if (value < INT_MIN)
92 return 0;
93 smp->type = SMP_T_SINT;
94 smp->data.sint = value;
95 }
96 else {
97 if (value > UINT_MAX)
98 return 0;
99 smp->type = SMP_T_UINT;
100 smp->data.uint = value;
101 }
102
103 return 1;
104}
105
106/* This function creates and initializes a new map_reference entry. This
107 * function only fails in case of a memory allocation issue, in which case
108 * it returns NULL. <reference> here is a unique identifier for the map's
109 * contents, typically the name of the file used to build the map.
110 */
111static struct map_reference *map_create_reference(const char *reference)
112{
113 struct map_reference *ref;
114
115 /* create new entry */
116 ref = calloc(1, sizeof(*ref));
117 if (!ref)
118 return NULL;
119
120 ref->reference = strdup(reference);
121 if (!ref->reference)
122 return NULL;
123
124 LIST_INIT(&ref->entries);
125 LIST_INIT(&ref->maps);
126 LIST_ADDQ(&maps, &ref->list);
127
128 return ref;
129}
130
131/* This function just create new entry */
132static struct map_entry *map_create_entry(int line, char *key, char *value)
133{
134 struct map_entry *ent;
135
136 ent = calloc(1, sizeof(*ent));
137 if (!ent)
138 return NULL;
139
140 ent->line = line;
141
142 ent->key = strdup(key);
143 if (!ent->key) {
144 free(ent);
145 return NULL;
146 }
147
148 ent->value = strdup(value);
149 if (!ent->value) {
150 free(ent->key);
151 free(ent);
152 return NULL;
153 }
154
155 return ent;
156}
157
158/* This crete and initialize map descriptor.
159 * Return NULL if out of memory error
160 */
161static struct map_descriptor *map_create_descriptor(struct map_reference *ref,
162 struct sample_conv *conv)
163{
164 struct map_descriptor *desc;
165
166 desc = calloc(1, sizeof(*desc));
167 if (!desc)
168 return NULL;
169
170 desc->conv = conv;
Thierry FOURNIERa82d2022013-12-05 11:59:55 +0100171 desc->ref = ref;
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100172
173 LIST_ADDQ(&ref->maps, &desc->list);
174
175 return desc;
176}
177
178/* This function just add entry into the list of pattern.
179 * It can return false only in memory problem case
180 */
181static int map_add_entry(struct map_reference *map, int line, char *key, char *value)
182{
183 struct map_entry *ent;
184
185 ent = map_create_entry(line, key, value);
186 if (!ent)
187 return 0;
188 LIST_ADDQ(&map->entries, &ent->list);
189 return 1;
190}
191
192/* Reads patterns from a file. If <err_msg> is non-NULL, an error message will
193 * be returned there on errors and the caller will have to free it.
194 *
195 * The file contains one key + value per line. Lines which start with '#' are
196 * ignored, just like empty lines. Leading tabs/spaces are stripped. The key is
197 * then the first "word" (series of non-space/tabs characters), and the value is
198 * what follows this series of space/tab till the end of the line excluding
199 * trailing spaces/tabs.
200 *
201 * Example :
202 *
203 * # this is a comment and is ignored
204 * 62.212.114.60 1wt.eu \n
205 * <-><-----------><---><----><---->
206 * | | | | `--- trailing spaces ignored
207 * | | | `-------- value
208 * | | `--------------- middle spaces ignored
209 * | `------------------------ key
210 * `-------------------------------- leading spaces ignored
211 *
212 * Return non-zero in case of succes, otherwise 0.
213 */
214static int map_read_entries_from_file(const char *filename,
215 struct map_reference *ref,
216 char **err)
217{
218 FILE *file;
219 char *c;
220 int ret = 0;
221 int line = 0;
222 char *key_beg;
223 char *key_end;
224 char *value_beg;
225 char *value_end;
226
227 file = fopen(filename, "r");
228 if (!file) {
229 memprintf(err, "failed to open pattern file <%s>", filename);
230 return 0;
231 }
232
233 /* now parse all patterns. The file may contain only one pattern
234 * followed by one value per line. The start spaces, separator spaces
235 * and and spaces are stripped. Each can contain comment started by '#'
236 */
237 while (fgets(trash.str, trash.size, file) != NULL) {
238 line++;
239 c = trash.str;
240
241 /* ignore lines beginning with a dash */
242 if (*c == '#')
243 continue;
244
245 /* strip leading spaces and tabs */
246 while (*c == ' ' || *c == '\t')
247 c++;
248
249 /* empty lines are ignored too */
250 if (*c == '\0')
251 continue;
252
253 /* look for the end of the key */
254 key_beg = c;
255 while (*c && *c != ' ' && *c != '\t' && *c != '\n' && *c != '\r')
256 c++;
257
258 key_end = c;
259
260 /* strip middle spaces and tabs */
261 while (*c == ' ' || *c == '\t')
262 c++;
263
264 /* look for the end of the value, it is the end of the line */
265 value_beg = c;
266 while (*c && *c != '\n' && *c != '\r')
267 c++;
268 value_end = c;
269
270 /* trim possibly trailing spaces and tabs */
271 while (value_end > value_beg && (value_end[-1] == ' ' || value_end[-1] == '\t'))
272 value_end--;
273
274 /* set final \0 and check entries */
275 *key_end = '\0';
276 *value_end = '\0';
277
278 /* insert values */
279 if (!map_add_entry(ref, line, key_beg, value_beg)) {
280 memprintf(err, "out of memory");
281 goto out_close;
282 }
283 }
284
285 /* succes */
286 ret = 1;
287
288 out_close:
289 fclose(file);
290 return ret;
291}
292
293/* This function read the string entries of <ent>, parse it with
294 * the <desc> methods, and strore the result into <desc> dummy ACL.
295 * return 1 in succes case, else return 0 and <err> is filled.
296 *
297 * The acm parser use <pattern> for creating new pattern (list
298 * of values case) or using the same pattern (tree index case).
299 *
300 * <patflags> must be PAT_F_*.
301 */
302static int map_parse_and_index(struct map_descriptor *desc,
303 struct pattern **pattern,
304 struct map_entry *ent,
305 int patflags,
306 char **err)
307{
308 struct sample_storage *smp;
Thierry FOURNIER7148ce62013-12-06 19:06:43 +0100309 const char *args[2];
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100310
311 /* use new smp for storing value */
312 smp = calloc(1, sizeof(*smp));
313 if (!smp)
314 return 0;
315
316 /* first read and convert value */
317 if (!desc->parse(ent->value, smp)) {
318 memprintf(err, "parse value failed at line %d of file <%s>",
319 ent->line, desc->ref->reference);
320 return 0;
321 }
322
Thierry FOURNIER7148ce62013-12-06 19:06:43 +0100323 /* register key */
324 args[0] = ent->key;
325 args[1] = "";
326 if (!pattern_register(desc->pat, args, smp, pattern, patflags, err))
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100327 return 0;
328
329 return 1;
330}
331
332/* This function load the map file according with data type declared into
333 * the "struct sample_conv".
334 *
335 * This function choose the indexation type (ebtree or list) according with
336 * the type of match needed.
337 */
338static int sample_load_map(struct arg *arg, struct sample_conv *conv, char **err)
339{
340 struct map_reference *ref;
341 struct map_descriptor *desc;
342 struct pattern *pattern;
343 struct map_entry *ent;
Thierry FOURNIER0ffe78c2013-12-05 14:40:25 +0100344 struct pattern_expr *pat = NULL;
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100345
346 /* look for existing map reference. The reference is the
347 * file encountered in the first argument. arg[0] with string
348 * type is guaranteed by the parser.
349 */
350 ref = map_get_reference(arg[0].data.str.str);
351
352 /* The reference doesn't exist */
353 if (!ref) {
354
355 /* create new reference entry */
356 ref = map_create_reference(arg[0].data.str.str);
357 if (!ref) {
358 memprintf(err, "out of memory");
359 return 0;
360 }
361
362 /* load the file */
363 if (!map_read_entries_from_file(arg[0].data.str.str, ref, err))
364 return 0;
365 }
366
Thierry FOURNIER0ffe78c2013-12-05 14:40:25 +0100367 /* look for identical existing map. Two maps are identical if
368 * their in_type and out_type are the same. If is not found, pat
369 * is NULL.
370 */
371 else {
372 list_for_each_entry(desc, &ref->maps, list)
373 if (desc->conv->in_type == conv->in_type &&
Thierry FOURNIER736459e2013-12-11 11:20:24 +0100374 desc->conv->out_type == conv->out_type &&
375 desc->conv->private == conv->private)
Thierry FOURNIER0ffe78c2013-12-05 14:40:25 +0100376 break;
377 if (&desc->list != &ref->maps)
378 pat = desc->pat;
379 }
380
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100381 /* create new map descriptor */
382 desc = map_create_descriptor(ref, conv);
383 if (!desc) {
384 memprintf(err, "out of memory");
385 return 0;
386 }
387
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100388 /* check the output parse method */
389 switch (desc->conv->out_type) {
390 case SMP_T_STR: desc->parse = map_parse_str; break;
391 case SMP_T_UINT: desc->parse = map_parse_int; break;
392 case SMP_T_IPV4: desc->parse = map_parse_ip; break;
393 case SMP_T_IPV6: desc->parse = map_parse_ip6; break;
394 default:
395 memprintf(err, "map: internal haproxy error: no default parse case for the input type <%d>.",
396 conv->out_type);
397 return 0;
398 }
399
Thierry FOURNIER0ffe78c2013-12-05 14:40:25 +0100400 /* If identical pattern is not found, initialize his own pattern */
401 if (!pat) {
402
403 desc->pat = calloc(1, sizeof(*desc->pat));
404 if (!desc->pat) {
405 memprintf(err, "out of memory");
406 return 0;
407 }
408
409 pattern_init_expr(desc->pat);
410
411 /* This is original pattern, must free */
412 desc->do_free = 1;
413
414 /* set the match method */
415 desc->pat->match = pat_match_fcts[conv->private];
Thierry FOURNIER736459e2013-12-11 11:20:24 +0100416 desc->pat->parse = pat_parse_fcts[conv->private];
Thierry FOURNIER0ffe78c2013-12-05 14:40:25 +0100417
418 /* parse each line of the file */
419 pattern = NULL;
420 list_for_each_entry(ent, &ref->entries, list)
421 if (!map_parse_and_index(desc, &pattern, ent, 0, err))
422 return 0;
423 }
424
425 /* identical pattern found. Use reference to this pattern, and mark
426 * the map_descriptor pattern as non freeable
427 */
428 else {
429 desc->pat = pat;
430 desc->do_free = 0;
431 }
432
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100433 /* The second argument is the default value */
434 if (arg[1].type == ARGT_STR) {
435 desc->default_value = strdup(arg[1].data.str.str);
436 if (!desc->default_value) {
437 memprintf(err, "out of memory");
438 return 0;
439 }
440 desc->def = calloc(1, sizeof(*desc->def));
441 if (!desc->def) {
442 memprintf(err, "out of memory");
443 return 0;
444 }
445 if (!desc->parse(desc->default_value, desc->def)) {
446 memprintf(err, "Cannot parse default value");
447 return 0;
448 }
449 }
450 else
451 desc->def = NULL;
452
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100453 /* replace the first argument by this definition */
454 arg[0].type = ARGT_MAP;
455 arg[0].data.map = desc;
456
457 return 1;
458}
459
460static int sample_conv_map(const struct arg *arg_p, struct sample *smp)
461{
462 struct map_descriptor *desc;
463 struct sample_storage *sample;
464 enum pat_match_res ret;
465
466 /* get config */
467 desc = arg_p[0].data.map;
468
469 /* Execute the match function. */
Thierry FOURNIER76090642013-12-10 15:03:38 +0100470 ret = pattern_exec_match(desc->pat, smp, &sample, NULL, NULL);
Thierry FOURNIERd5f624d2013-11-26 11:52:33 +0100471 if (ret != PAT_MATCH) {
472 if (!desc->def)
473 return 0;
474 sample = desc->def;
475 }
476
477 /* copy new data */
478 smp->type = sample->type;
479 memcpy(&smp->data, &sample->data, sizeof(smp->data));
480 return 1;
481}
482
483/* Note: must not be declared <const> as its list will be overwritten
484 *
485 * For the map_*_int keywords, the output is declared as SMP_T_UINT, but the converter function
486 * can provide SMP_T_UINT, SMP_T_SINT or SMP_T_BOOL depending on how the patterns found in the
487 * file can be parsed.
488 *
489 * For the map_*_ip keyword, the output is declared as SMP_T_IPV4, but the converter function
490 * can provide SMP_T_IPV4 or SMP_T_IPV6 depending on the patterns found in the file.
491 *
492 * The map_* keywords only emit strings.
493 *
494 * The output type is only used during the configuration parsing. It is used for detecting
495 * compatibility problems.
496 *
497 * The arguments are: <file>[,<default value>]
498 */
499static struct sample_conv_kw_list sample_conv_kws = {ILH, {
500 { "map", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_STR },
501 { "map_str", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_STR },
502 { "map_beg", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_BEG },
503 { "map_sub", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_SUB },
504 { "map_dir", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_DIR },
505 { "map_dom", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_DOM },
506 { "map_end", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_END },
507 { "map_reg", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_STR, PAT_MATCH_REG },
508 { "map_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_UINT, SMP_T_STR, PAT_MATCH_INT },
509 { "map_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_ADDR, SMP_T_STR, PAT_MATCH_IP },
510
511 { "map_str_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_UINT, PAT_MATCH_STR },
512 { "map_beg_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_UINT, PAT_MATCH_BEG },
513 { "map_sub_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_UINT, PAT_MATCH_SUB },
514 { "map_dir_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_UINT, PAT_MATCH_DIR },
515 { "map_dom_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_UINT, PAT_MATCH_DOM },
516 { "map_end_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_UINT, PAT_MATCH_END },
517 { "map_reg_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_UINT, PAT_MATCH_REG },
518 { "map_int_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_UINT, SMP_T_UINT, PAT_MATCH_INT },
519 { "map_ip_int", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_ADDR, SMP_T_UINT, PAT_MATCH_IP },
520
521 { "map_str_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_IPV4, PAT_MATCH_STR },
522 { "map_beg_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_IPV4, PAT_MATCH_BEG },
523 { "map_sub_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_IPV4, PAT_MATCH_SUB },
524 { "map_dir_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_IPV4, PAT_MATCH_DIR },
525 { "map_dom_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_IPV4, PAT_MATCH_DOM },
526 { "map_end_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_IPV4, PAT_MATCH_END },
527 { "map_reg_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_STR, SMP_T_IPV4, PAT_MATCH_REG },
528 { "map_int_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_UINT, SMP_T_IPV4, PAT_MATCH_INT },
529 { "map_ip_ip", sample_conv_map, ARG2(1,STR,STR), sample_load_map, SMP_T_ADDR, SMP_T_IPV4, PAT_MATCH_IP },
530
531 { /* END */ },
532}};
533
534__attribute__((constructor))
535static void __map_init(void)
536{
537 /* register format conversion keywords */
538 sample_register_convs(&sample_conv_kws);
539}