blob: 16fd6809c3a7207247e054eb4c09f08262f6be0d [file] [log] [blame]
Dragan Dosen93b38d92015-06-29 16:43:25 +02001#include <stdio.h>
2
3#include <common/cfgparse.h>
4#include <common/chunk.h>
5#include <proto/arg.h>
6#include <proto/log.h>
7#include <proto/sample.h>
8#include <import/xxhash.h>
Dragan Dosen105c8e62015-06-29 16:43:26 +02009#include <import/lru.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +020010
11#include <import/51d.h>
12
13struct _51d_property_names {
14 struct list list;
15 char *name;
16};
17
Dragan Dosen105c8e62015-06-29 16:43:26 +020018static struct lru64_head *_51d_lru_tree = NULL;
19static unsigned long long _51d_lru_seed;
20
Dragan Dosen93b38d92015-06-29 16:43:25 +020021static int _51d_data_file(char **args, int section_type, struct proxy *curpx,
22 struct proxy *defpx, const char *file, int line,
23 char **err)
24{
25 if (*(args[1]) == 0) {
26 memprintf(err,
27 "'%s' expects a filepath to a 51Degrees trie or pattern data file.",
28 args[0]);
29 return -1;
30 }
31
32 if (global._51degrees.data_file_path)
33 free(global._51degrees.data_file_path);
34 global._51degrees.data_file_path = strdup(args[1]);
35
36 return 0;
37}
38
39static int _51d_property_name_list(char **args, int section_type, struct proxy *curpx,
40 struct proxy *defpx, const char *file, int line,
41 char **err)
42{
43 int cur_arg = 1;
44 struct _51d_property_names *name;
45
46 if (*(args[cur_arg]) == 0) {
47 memprintf(err,
48 "'%s' expects at least one 51Degrees property name.",
49 args[0]);
50 return -1;
51 }
52
53 while (*(args[cur_arg])) {
54 name = calloc(1, sizeof(struct _51d_property_names));
55 name->name = strdup(args[cur_arg]);
56 LIST_ADDQ(&global._51degrees.property_names, &name->list);
57 ++cur_arg;
58 }
59
60 return 0;
61}
62
63static int _51d_property_separator(char **args, int section_type, struct proxy *curpx,
64 struct proxy *defpx, const char *file, int line,
65 char **err)
66{
67 if (*(args[1]) == 0) {
68 memprintf(err,
69 "'%s' expects a single character.",
70 args[0]);
71 return -1;
72 }
73 if (strlen(args[1]) > 1) {
74 memprintf(err,
75 "'%s' expects a single character, got '%s'.",
76 args[0], args[1]);
77 return -1;
78 }
79
80 global._51degrees.property_separator = *args[1];
81
82 return 0;
83}
84
Dragan Dosen105c8e62015-06-29 16:43:26 +020085static int _51d_cache_size(char **args, int section_type, struct proxy *curpx,
86 struct proxy *defpx, const char *file, int line,
87 char **err)
88{
89 if (*(args[1]) == 0) {
90 memprintf(err,
91 "'%s' expects a positive numeric value.",
92 args[0]);
93 return -1;
94 }
95
96 global._51degrees.cache_size = atoi(args[1]);
97 if (global._51degrees.cache_size < 0) {
98 memprintf(err,
99 "'%s' expects a positive numeric value, got '%s'.",
100 args[0], args[1]);
101 return -1;
102 }
103
104 return 0;
105}
106
Dragan Dosen9373fc52015-08-07 16:41:23 +0200107static int _51d_conv_check(struct arg *arg, struct sample_conv *conv,
108 const char *file, int line, char **err)
109{
110 if (global._51degrees.data_file_path)
111 return 1;
112
113 memprintf(err, "51Degrees data file is not specified (parameter '51degrees-data-file')");
114 return 0;
115}
116
Dragan Dosen93b38d92015-06-29 16:43:25 +0200117static int _51d_conv(const struct arg *args, struct sample *smp, void *private)
118{
119 int i;
120 char no_data[] = "NoData"; /* response when no data could be found */
121 struct chunk *temp;
122#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
123 int j, found;
124 const char* property_name;
125 fiftyoneDegreesWorkset* ws; /* workset for detection */
126#endif
127#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
128 int device_offset;
129 int property_index;
130#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +0200131 struct lru64 *lru = NULL;
132
133 /* Look in the list. */
134 if (_51d_lru_tree) {
135 unsigned long long seed = _51d_lru_seed ^ (long)args;
136
Thierry FOURNIER2046c462015-08-20 13:42:12 +0200137 lru = lru64_get(XXH64(smp->data.u.str.str, smp->data.u.str.len, seed),
Dragan Dosen105c8e62015-06-29 16:43:26 +0200138 _51d_lru_tree, global._51degrees.data_file_path, 0);
139 if (lru && lru->domain) {
Dragan Dosen96a0be72015-07-07 16:10:43 +0200140 smp->flags |= SMP_F_CONST;
Thierry FOURNIER2046c462015-08-20 13:42:12 +0200141 smp->data.u.str.str = lru->data;
142 smp->data.u.str.len = strlen(smp->data.u.str.str);
Dragan Dosen105c8e62015-06-29 16:43:26 +0200143 return 1;
144 }
145 }
Dragan Dosen93b38d92015-06-29 16:43:25 +0200146
147#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
148 /* Create workset. This will later contain detection results. */
149 ws = fiftyoneDegreesCreateWorkset(&global._51degrees.data_set);
150 if (!ws)
151 return 0;
152#endif
153
Dragan Dosen96a0be72015-07-07 16:10:43 +0200154 /* Duplicate the data and remove the "const" flag before device detection. */
155 if (!smp_dup(smp))
156 return 0;
157
Thierry FOURNIER2046c462015-08-20 13:42:12 +0200158 smp->data.u.str.str[smp->data.u.str.len] = '\0';
Dragan Dosen93b38d92015-06-29 16:43:25 +0200159
160 /* Perform detection. */
161#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Thierry FOURNIER2046c462015-08-20 13:42:12 +0200162 fiftyoneDegreesMatch(ws, smp->data.u.str.str);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200163#endif
164#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
Thierry FOURNIER2046c462015-08-20 13:42:12 +0200165 device_offset = fiftyoneDegreesGetDeviceOffset(smp->data.u.str.str);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200166#endif
167
168 i = 0;
169 temp = get_trash_chunk();
Dragan Dosen93b38d92015-06-29 16:43:25 +0200170
171 /* Loop through property names passed to the filter and fetch them from the dataset. */
172 while (args[i].data.str.str) {
173 /* Try to find request property in dataset. */
174#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
175 found = 0;
176 for (j = 0; j < ws->dataSet->requiredPropertyCount; j++) {
177 property_name = fiftyoneDegreesGetPropertyName(ws->dataSet, ws->dataSet->requiredProperties[j]);
178 if (strcmp(property_name, args[i].data.str.str) == 0) {
179 found = 1;
180 fiftyoneDegreesSetValues(ws, j);
181 chunk_appendf(temp, "%s", fiftyoneDegreesGetValueName(ws->dataSet, *ws->values));
182 break;
183 }
184 }
185 if (!found) {
186 chunk_appendf(temp, "%s", no_data);
187 }
188#endif
189#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
190 property_index = fiftyoneDegreesGetPropertyIndex(args[i].data.str.str);
191 if (property_index > 0) {
192 chunk_appendf(temp, "%s", fiftyoneDegreesGetValue(device_offset, property_index));
193 }
194 else {
195 chunk_appendf(temp, "%s", no_data);
196 }
197#endif
198 /* Add separator. */
199 chunk_appendf(temp, "%c", global._51degrees.property_separator);
200 ++i;
201 }
202
203 if (temp->len) {
204 --temp->len;
205 temp->str[temp->len] = '\0';
206 }
207
Thierry FOURNIER2046c462015-08-20 13:42:12 +0200208 smp->data.u.str.str = temp->str;
209 smp->data.u.str.len = strlen(smp->data.u.str.str);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200210
211#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
212 fiftyoneDegreesFreeWorkset(ws);
213#endif
214
Dragan Dosen96a0be72015-07-07 16:10:43 +0200215 if (lru) {
216 smp->flags |= SMP_F_CONST;
Thierry FOURNIER2046c462015-08-20 13:42:12 +0200217 lru64_commit(lru, strdup(smp->data.u.str.str), global._51degrees.data_file_path, 0, free);
Dragan Dosen96a0be72015-07-07 16:10:43 +0200218 }
Dragan Dosen105c8e62015-06-29 16:43:26 +0200219
Dragan Dosen93b38d92015-06-29 16:43:25 +0200220 return 1;
221}
222
223int init_51degrees(void)
224{
225 int i = 0;
226 struct chunk *temp;
227 struct _51d_property_names *name;
228 char **_51d_property_list = NULL;
229 fiftyoneDegreesDataSetInitStatus _51d_dataset_status = DATA_SET_INIT_STATUS_NOT_SET;
230
Dragan Dosen9373fc52015-08-07 16:41:23 +0200231 if (!global._51degrees.data_file_path)
232 return -1;
233
Dragan Dosen93b38d92015-06-29 16:43:25 +0200234 if (!LIST_ISEMPTY(&global._51degrees.property_names)) {
235 i = 0;
236 list_for_each_entry(name, &global._51degrees.property_names, list)
237 ++i;
238 _51d_property_list = calloc(i, sizeof(char *));
239
240 i = 0;
241 list_for_each_entry(name, &global._51degrees.property_names, list)
242 _51d_property_list[i++] = name->name;
243 }
244
245#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
246 _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global._51degrees.data_file_path, &global._51degrees.data_set, _51d_property_list, i);
247#endif
248#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
249 _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global._51degrees.data_file_path, _51d_property_list, i);
250#endif
251
252 temp = get_trash_chunk();
253 chunk_reset(temp);
254
255 switch (_51d_dataset_status) {
256 case DATA_SET_INIT_STATUS_SUCCESS:
257 break;
258 case DATA_SET_INIT_STATUS_INSUFFICIENT_MEMORY:
259 chunk_printf(temp, "Insufficient memory.");
260 break;
261 case DATA_SET_INIT_STATUS_CORRUPT_DATA:
262#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
263 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Pattern data format.");
264#endif
265#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
266 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Trie data format.");
267#endif
268 break;
269 case DATA_SET_INIT_STATUS_INCORRECT_VERSION:
270#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
271 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Pattern data format.");
272#endif
273#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
274 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Trie data format.");
275#endif
276 break;
277 case DATA_SET_INIT_STATUS_FILE_NOT_FOUND:
278 chunk_printf(temp, "File not found.");
279 break;
280 case DATA_SET_INIT_STATUS_NOT_SET:
281 chunk_printf(temp, "Data set not initialised.");
282 break;
283 }
284 if (_51d_dataset_status != DATA_SET_INIT_STATUS_SUCCESS) {
285 if (temp->len)
286 Alert("51Degrees Setup - Error reading 51Degrees data file. %s\n", temp->str);
287 else
288 Alert("51Degrees Setup - Error reading 51Degrees data file.\n");
289 exit(1);
290 }
291 free(_51d_property_list);
292
Dragan Dosen105c8e62015-06-29 16:43:26 +0200293 _51d_lru_seed = random();
294 if (global._51degrees.cache_size)
295 _51d_lru_tree = lru64_new(global._51degrees.cache_size);
296
Dragan Dosen93b38d92015-06-29 16:43:25 +0200297 return 0;
298}
299
300void deinit_51degrees(void)
301{
302 struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
303
304#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
305 fiftyoneDegreesDestroy(&global._51degrees.data_set);
306#endif
307#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
308 fiftyoneDegreesDestroy();
309#endif
310
311 free(global._51degrees.data_file_path); global._51degrees.data_file_path = NULL;
312 list_for_each_entry_safe(_51d_prop_name, _51d_prop_nameb, &global._51degrees.property_names, list) {
313 LIST_DEL(&_51d_prop_name->list);
314 free(_51d_prop_name);
315 }
Dragan Dosen105c8e62015-06-29 16:43:26 +0200316
317 while (lru64_destroy(_51d_lru_tree));
Dragan Dosen93b38d92015-06-29 16:43:25 +0200318}
319
320static struct cfg_kw_list _51dcfg_kws = {{ }, {
321 { CFG_GLOBAL, "51degrees-data-file", _51d_data_file },
322 { CFG_GLOBAL, "51degrees-property-name-list", _51d_property_name_list },
323 { CFG_GLOBAL, "51degrees-property-separator", _51d_property_separator },
Dragan Dosen105c8e62015-06-29 16:43:26 +0200324 { CFG_GLOBAL, "51degrees-cache-size", _51d_cache_size },
Dragan Dosen93b38d92015-06-29 16:43:25 +0200325 { 0, NULL, NULL },
326}};
327
328/* Note: must not be declared <const> as its list will be overwritten */
329static struct sample_conv_kw_list conv_kws = {ILH, {
Dragan Dosen9373fc52015-08-07 16:41:23 +0200330 { "51d", _51d_conv, ARG5(1,STR,STR,STR,STR,STR), _51d_conv_check, SMP_T_STR, SMP_T_STR },
Dragan Dosen93b38d92015-06-29 16:43:25 +0200331 { NULL, NULL, 0, 0, 0 },
332}};
333
334__attribute__((constructor))
335static void __51d_init(void)
336{
337 /* register sample fetch and format conversion keywords */
338 sample_register_convs(&conv_kws);
339 cfg_register_keywords(&_51dcfg_kws);
340}