blob: abb5cce9eb07262c962b0f8094602dbb5dcf9287 [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) {
130 smp->data.str.str = lru->data;
131 smp->data.str.len = strlen(smp->data.str.str);
132 return 1;
133 }
134 }
Dragan Dosen93b38d92015-06-29 16:43:25 +0200135
136#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
137 /* Create workset. This will later contain detection results. */
138 ws = fiftyoneDegreesCreateWorkset(&global._51degrees.data_set);
139 if (!ws)
140 return 0;
141#endif
142
143 smp->data.str.str[smp->data.str.len] = '\0';
144
145 /* Perform detection. */
146#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
147 fiftyoneDegreesMatch(ws, smp->data.str.str);
148#endif
149#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
150 device_offset = fiftyoneDegreesGetDeviceOffset(smp->data.str.str);
151#endif
152
153 i = 0;
154 temp = get_trash_chunk();
155 chunk_reset(temp);
156
157 /* Loop through property names passed to the filter and fetch them from the dataset. */
158 while (args[i].data.str.str) {
159 /* Try to find request property in dataset. */
160#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
161 found = 0;
162 for (j = 0; j < ws->dataSet->requiredPropertyCount; j++) {
163 property_name = fiftyoneDegreesGetPropertyName(ws->dataSet, ws->dataSet->requiredProperties[j]);
164 if (strcmp(property_name, args[i].data.str.str) == 0) {
165 found = 1;
166 fiftyoneDegreesSetValues(ws, j);
167 chunk_appendf(temp, "%s", fiftyoneDegreesGetValueName(ws->dataSet, *ws->values));
168 break;
169 }
170 }
171 if (!found) {
172 chunk_appendf(temp, "%s", no_data);
173 }
174#endif
175#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
176 property_index = fiftyoneDegreesGetPropertyIndex(args[i].data.str.str);
177 if (property_index > 0) {
178 chunk_appendf(temp, "%s", fiftyoneDegreesGetValue(device_offset, property_index));
179 }
180 else {
181 chunk_appendf(temp, "%s", no_data);
182 }
183#endif
184 /* Add separator. */
185 chunk_appendf(temp, "%c", global._51degrees.property_separator);
186 ++i;
187 }
188
189 if (temp->len) {
190 --temp->len;
191 temp->str[temp->len] = '\0';
192 }
193
194 smp->data.str.str = temp->str;
195 smp->data.str.len = strlen(smp->data.str.str);
196
197#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
198 fiftyoneDegreesFreeWorkset(ws);
199#endif
200
Dragan Dosen105c8e62015-06-29 16:43:26 +0200201 if (lru)
202 lru64_commit(lru, strdup(smp->data.str.str), global._51degrees.data_file_path, 0, free);
203
Dragan Dosen93b38d92015-06-29 16:43:25 +0200204 return 1;
205}
206
207int init_51degrees(void)
208{
209 int i = 0;
210 struct chunk *temp;
211 struct _51d_property_names *name;
212 char **_51d_property_list = NULL;
213 fiftyoneDegreesDataSetInitStatus _51d_dataset_status = DATA_SET_INIT_STATUS_NOT_SET;
214
215 if (!LIST_ISEMPTY(&global._51degrees.property_names)) {
216 i = 0;
217 list_for_each_entry(name, &global._51degrees.property_names, list)
218 ++i;
219 _51d_property_list = calloc(i, sizeof(char *));
220
221 i = 0;
222 list_for_each_entry(name, &global._51degrees.property_names, list)
223 _51d_property_list[i++] = name->name;
224 }
225
226#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
227 _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global._51degrees.data_file_path, &global._51degrees.data_set, _51d_property_list, i);
228#endif
229#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
230 _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global._51degrees.data_file_path, _51d_property_list, i);
231#endif
232
233 temp = get_trash_chunk();
234 chunk_reset(temp);
235
236 switch (_51d_dataset_status) {
237 case DATA_SET_INIT_STATUS_SUCCESS:
238 break;
239 case DATA_SET_INIT_STATUS_INSUFFICIENT_MEMORY:
240 chunk_printf(temp, "Insufficient memory.");
241 break;
242 case DATA_SET_INIT_STATUS_CORRUPT_DATA:
243#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
244 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Pattern data format.");
245#endif
246#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
247 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Trie data format.");
248#endif
249 break;
250 case DATA_SET_INIT_STATUS_INCORRECT_VERSION:
251#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
252 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Pattern data format.");
253#endif
254#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
255 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Trie data format.");
256#endif
257 break;
258 case DATA_SET_INIT_STATUS_FILE_NOT_FOUND:
259 chunk_printf(temp, "File not found.");
260 break;
261 case DATA_SET_INIT_STATUS_NOT_SET:
262 chunk_printf(temp, "Data set not initialised.");
263 break;
264 }
265 if (_51d_dataset_status != DATA_SET_INIT_STATUS_SUCCESS) {
266 if (temp->len)
267 Alert("51Degrees Setup - Error reading 51Degrees data file. %s\n", temp->str);
268 else
269 Alert("51Degrees Setup - Error reading 51Degrees data file.\n");
270 exit(1);
271 }
272 free(_51d_property_list);
273
Dragan Dosen105c8e62015-06-29 16:43:26 +0200274 _51d_lru_seed = random();
275 if (global._51degrees.cache_size)
276 _51d_lru_tree = lru64_new(global._51degrees.cache_size);
277
Dragan Dosen93b38d92015-06-29 16:43:25 +0200278 return 0;
279}
280
281void deinit_51degrees(void)
282{
283 struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
284
285#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
286 fiftyoneDegreesDestroy(&global._51degrees.data_set);
287#endif
288#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
289 fiftyoneDegreesDestroy();
290#endif
291
292 free(global._51degrees.data_file_path); global._51degrees.data_file_path = NULL;
293 list_for_each_entry_safe(_51d_prop_name, _51d_prop_nameb, &global._51degrees.property_names, list) {
294 LIST_DEL(&_51d_prop_name->list);
295 free(_51d_prop_name);
296 }
Dragan Dosen105c8e62015-06-29 16:43:26 +0200297
298 while (lru64_destroy(_51d_lru_tree));
Dragan Dosen93b38d92015-06-29 16:43:25 +0200299}
300
301static struct cfg_kw_list _51dcfg_kws = {{ }, {
302 { CFG_GLOBAL, "51degrees-data-file", _51d_data_file },
303 { CFG_GLOBAL, "51degrees-property-name-list", _51d_property_name_list },
304 { CFG_GLOBAL, "51degrees-property-separator", _51d_property_separator },
Dragan Dosen105c8e62015-06-29 16:43:26 +0200305 { CFG_GLOBAL, "51degrees-cache-size", _51d_cache_size },
Dragan Dosen93b38d92015-06-29 16:43:25 +0200306 { 0, NULL, NULL },
307}};
308
309/* Note: must not be declared <const> as its list will be overwritten */
310static struct sample_conv_kw_list conv_kws = {ILH, {
311 { "51d", _51d_conv, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR },
312 { NULL, NULL, 0, 0, 0 },
313}};
314
315__attribute__((constructor))
316static void __51d_init(void)
317{
318 /* register sample fetch and format conversion keywords */
319 sample_register_convs(&conv_kws);
320 cfg_register_keywords(&_51dcfg_kws);
321}