blob: 6f17c28b8479b0edeab48387a3a3e679666909fd [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 Dosen93b38d92015-06-29 16:43:25 +0200107static int _51d_conv(const struct arg *args, struct sample *smp, void *private)
108{
109 int i;
110 char no_data[] = "NoData"; /* response when no data could be found */
111 struct chunk *temp;
112#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
113 int j, found;
114 const char* property_name;
115 fiftyoneDegreesWorkset* ws; /* workset for detection */
116#endif
117#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
118 int device_offset;
119 int property_index;
120#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +0200121 struct lru64 *lru = NULL;
122
123 /* Look in the list. */
124 if (_51d_lru_tree) {
125 unsigned long long seed = _51d_lru_seed ^ (long)args;
126
127 lru = lru64_get(XXH64(smp->data.str.str, smp->data.str.len, seed),
128 _51d_lru_tree, global._51degrees.data_file_path, 0);
129 if (lru && lru->domain) {
Dragan Dosen96a0be72015-07-07 16:10:43 +0200130 smp->flags |= SMP_F_CONST;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200131 smp->data.str.str = lru->data;
132 smp->data.str.len = strlen(smp->data.str.str);
133 return 1;
134 }
135 }
Dragan Dosen93b38d92015-06-29 16:43:25 +0200136
137#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
138 /* Create workset. This will later contain detection results. */
139 ws = fiftyoneDegreesCreateWorkset(&global._51degrees.data_set);
140 if (!ws)
141 return 0;
142#endif
143
Dragan Dosen96a0be72015-07-07 16:10:43 +0200144 /* Duplicate the data and remove the "const" flag before device detection. */
145 if (!smp_dup(smp))
146 return 0;
147
Dragan Dosen93b38d92015-06-29 16:43:25 +0200148 smp->data.str.str[smp->data.str.len] = '\0';
149
150 /* Perform detection. */
151#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
152 fiftyoneDegreesMatch(ws, smp->data.str.str);
153#endif
154#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
155 device_offset = fiftyoneDegreesGetDeviceOffset(smp->data.str.str);
156#endif
157
158 i = 0;
159 temp = get_trash_chunk();
Dragan Dosen93b38d92015-06-29 16:43:25 +0200160
161 /* Loop through property names passed to the filter and fetch them from the dataset. */
162 while (args[i].data.str.str) {
163 /* Try to find request property in dataset. */
164#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
165 found = 0;
166 for (j = 0; j < ws->dataSet->requiredPropertyCount; j++) {
167 property_name = fiftyoneDegreesGetPropertyName(ws->dataSet, ws->dataSet->requiredProperties[j]);
168 if (strcmp(property_name, args[i].data.str.str) == 0) {
169 found = 1;
170 fiftyoneDegreesSetValues(ws, j);
171 chunk_appendf(temp, "%s", fiftyoneDegreesGetValueName(ws->dataSet, *ws->values));
172 break;
173 }
174 }
175 if (!found) {
176 chunk_appendf(temp, "%s", no_data);
177 }
178#endif
179#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
180 property_index = fiftyoneDegreesGetPropertyIndex(args[i].data.str.str);
181 if (property_index > 0) {
182 chunk_appendf(temp, "%s", fiftyoneDegreesGetValue(device_offset, property_index));
183 }
184 else {
185 chunk_appendf(temp, "%s", no_data);
186 }
187#endif
188 /* Add separator. */
189 chunk_appendf(temp, "%c", global._51degrees.property_separator);
190 ++i;
191 }
192
193 if (temp->len) {
194 --temp->len;
195 temp->str[temp->len] = '\0';
196 }
197
198 smp->data.str.str = temp->str;
199 smp->data.str.len = strlen(smp->data.str.str);
200
201#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
202 fiftyoneDegreesFreeWorkset(ws);
203#endif
204
Dragan Dosen96a0be72015-07-07 16:10:43 +0200205 if (lru) {
206 smp->flags |= SMP_F_CONST;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200207 lru64_commit(lru, strdup(smp->data.str.str), global._51degrees.data_file_path, 0, free);
Dragan Dosen96a0be72015-07-07 16:10:43 +0200208 }
Dragan Dosen105c8e62015-06-29 16:43:26 +0200209
Dragan Dosen93b38d92015-06-29 16:43:25 +0200210 return 1;
211}
212
213int init_51degrees(void)
214{
215 int i = 0;
216 struct chunk *temp;
217 struct _51d_property_names *name;
218 char **_51d_property_list = NULL;
219 fiftyoneDegreesDataSetInitStatus _51d_dataset_status = DATA_SET_INIT_STATUS_NOT_SET;
220
221 if (!LIST_ISEMPTY(&global._51degrees.property_names)) {
222 i = 0;
223 list_for_each_entry(name, &global._51degrees.property_names, list)
224 ++i;
225 _51d_property_list = calloc(i, sizeof(char *));
226
227 i = 0;
228 list_for_each_entry(name, &global._51degrees.property_names, list)
229 _51d_property_list[i++] = name->name;
230 }
231
232#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
233 _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global._51degrees.data_file_path, &global._51degrees.data_set, _51d_property_list, i);
234#endif
235#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
236 _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global._51degrees.data_file_path, _51d_property_list, i);
237#endif
238
239 temp = get_trash_chunk();
240 chunk_reset(temp);
241
242 switch (_51d_dataset_status) {
243 case DATA_SET_INIT_STATUS_SUCCESS:
244 break;
245 case DATA_SET_INIT_STATUS_INSUFFICIENT_MEMORY:
246 chunk_printf(temp, "Insufficient memory.");
247 break;
248 case DATA_SET_INIT_STATUS_CORRUPT_DATA:
249#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
250 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Pattern data format.");
251#endif
252#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
253 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Trie data format.");
254#endif
255 break;
256 case DATA_SET_INIT_STATUS_INCORRECT_VERSION:
257#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
258 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Pattern data format.");
259#endif
260#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
261 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Trie data format.");
262#endif
263 break;
264 case DATA_SET_INIT_STATUS_FILE_NOT_FOUND:
265 chunk_printf(temp, "File not found.");
266 break;
267 case DATA_SET_INIT_STATUS_NOT_SET:
268 chunk_printf(temp, "Data set not initialised.");
269 break;
270 }
271 if (_51d_dataset_status != DATA_SET_INIT_STATUS_SUCCESS) {
272 if (temp->len)
273 Alert("51Degrees Setup - Error reading 51Degrees data file. %s\n", temp->str);
274 else
275 Alert("51Degrees Setup - Error reading 51Degrees data file.\n");
276 exit(1);
277 }
278 free(_51d_property_list);
279
Dragan Dosen105c8e62015-06-29 16:43:26 +0200280 _51d_lru_seed = random();
281 if (global._51degrees.cache_size)
282 _51d_lru_tree = lru64_new(global._51degrees.cache_size);
283
Dragan Dosen93b38d92015-06-29 16:43:25 +0200284 return 0;
285}
286
287void deinit_51degrees(void)
288{
289 struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
290
291#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
292 fiftyoneDegreesDestroy(&global._51degrees.data_set);
293#endif
294#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
295 fiftyoneDegreesDestroy();
296#endif
297
298 free(global._51degrees.data_file_path); global._51degrees.data_file_path = NULL;
299 list_for_each_entry_safe(_51d_prop_name, _51d_prop_nameb, &global._51degrees.property_names, list) {
300 LIST_DEL(&_51d_prop_name->list);
301 free(_51d_prop_name);
302 }
Dragan Dosen105c8e62015-06-29 16:43:26 +0200303
304 while (lru64_destroy(_51d_lru_tree));
Dragan Dosen93b38d92015-06-29 16:43:25 +0200305}
306
307static struct cfg_kw_list _51dcfg_kws = {{ }, {
308 { CFG_GLOBAL, "51degrees-data-file", _51d_data_file },
309 { CFG_GLOBAL, "51degrees-property-name-list", _51d_property_name_list },
310 { CFG_GLOBAL, "51degrees-property-separator", _51d_property_separator },
Dragan Dosen105c8e62015-06-29 16:43:26 +0200311 { CFG_GLOBAL, "51degrees-cache-size", _51d_cache_size },
Dragan Dosen93b38d92015-06-29 16:43:25 +0200312 { 0, NULL, NULL },
313}};
314
315/* Note: must not be declared <const> as its list will be overwritten */
316static struct sample_conv_kw_list conv_kws = {ILH, {
317 { "51d", _51d_conv, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR },
318 { NULL, NULL, 0, 0, 0 },
319}};
320
321__attribute__((constructor))
322static void __51d_init(void)
323{
324 /* register sample fetch and format conversion keywords */
325 sample_register_convs(&conv_kws);
326 cfg_register_keywords(&_51dcfg_kws);
327}