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