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