blob: d537964880cb9fadaed54239d599e3a1edc266c8 [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>
James Rosewella28bbd52015-09-18 18:28:52 +01005#include <common/buffer.h>
Willy Tarreau9f3f2542016-12-21 20:30:05 +01006#include <common/errors.h>
Willy Tarreau0108d902018-11-25 19:14:37 +01007#include <common/initcall.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +02008#include <proto/arg.h>
Willy Tarreau79e57332018-10-02 16:01:16 +02009#include <proto/http_fetch.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +020010#include <proto/log.h>
James Rosewella28bbd52015-09-18 18:28:52 +010011#include <proto/proto_http.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +020012#include <proto/sample.h>
13#include <import/xxhash.h>
Dragan Dosen105c8e62015-06-29 16:43:26 +020014#include <import/lru.h>
Willy Tarreaub7a67142016-12-21 21:18:44 +010015#include <51Degrees.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +020016
17struct _51d_property_names {
18 struct list list;
19 char *name;
20};
21
James Rosewella28bbd52015-09-18 18:28:52 +010022#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Dragan Dosen105c8e62015-06-29 16:43:26 +020023static struct lru64_head *_51d_lru_tree = NULL;
24static unsigned long long _51d_lru_seed;
James Rosewella28bbd52015-09-18 18:28:52 +010025#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +020026
Willy Tarreaub7a67142016-12-21 21:18:44 +010027static struct {
28 char property_separator; /* the separator to use in the response for the values. this is taken from 51degrees-property-separator from config. */
29 struct list property_names; /* list of properties to load into the data set. this is taken from 51degrees-property-name-list from config. */
30 char *data_file_path;
31 int header_count; /* number of HTTP headers related to device detection. */
Willy Tarreau83061a82018-07-13 11:56:34 +020032 struct buffer *header_names; /* array of HTTP header names. */
Willy Tarreaub7a67142016-12-21 21:18:44 +010033 fiftyoneDegreesDataSet data_set; /* data set used with the pattern and trie detection methods. */
34#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
35 fiftyoneDegreesWorksetPool *pool; /* pool of worksets to avoid creating a new one for each request. */
36#endif
37#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
38 int32_t *header_offsets; /* offsets to the HTTP header name string. */
39 fiftyoneDegreesDeviceOffsets device_offsets; /* Memory used for device offsets. */
40#endif
41 int cache_size;
42} global_51degrees = {
43 .property_separator = ',',
44 .property_names = LIST_HEAD_INIT(global_51degrees.property_names),
45 .data_file_path = NULL,
46#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
47 .data_set = { },
48#endif
49 .cache_size = 0,
50};
51
Dragan Dosen93b38d92015-06-29 16:43:25 +020052static int _51d_data_file(char **args, int section_type, struct proxy *curpx,
53 struct proxy *defpx, const char *file, int line,
54 char **err)
55{
56 if (*(args[1]) == 0) {
57 memprintf(err,
58 "'%s' expects a filepath to a 51Degrees trie or pattern data file.",
59 args[0]);
60 return -1;
61 }
62
Willy Tarreaub7a67142016-12-21 21:18:44 +010063 if (global_51degrees.data_file_path)
64 free(global_51degrees.data_file_path);
65 global_51degrees.data_file_path = strdup(args[1]);
Dragan Dosen93b38d92015-06-29 16:43:25 +020066
67 return 0;
68}
69
70static int _51d_property_name_list(char **args, int section_type, struct proxy *curpx,
James Rosewella28bbd52015-09-18 18:28:52 +010071 struct proxy *defpx, const char *file, int line,
72 char **err)
Dragan Dosen93b38d92015-06-29 16:43:25 +020073{
74 int cur_arg = 1;
75 struct _51d_property_names *name;
76
77 if (*(args[cur_arg]) == 0) {
78 memprintf(err,
79 "'%s' expects at least one 51Degrees property name.",
80 args[0]);
81 return -1;
82 }
83
84 while (*(args[cur_arg])) {
Vincent Bernat02779b62016-04-03 13:48:43 +020085 name = calloc(1, sizeof(*name));
Dragan Dosen93b38d92015-06-29 16:43:25 +020086 name->name = strdup(args[cur_arg]);
Willy Tarreaub7a67142016-12-21 21:18:44 +010087 LIST_ADDQ(&global_51degrees.property_names, &name->list);
Dragan Dosen93b38d92015-06-29 16:43:25 +020088 ++cur_arg;
89 }
90
91 return 0;
92}
93
94static int _51d_property_separator(char **args, int section_type, struct proxy *curpx,
95 struct proxy *defpx, const char *file, int line,
96 char **err)
97{
98 if (*(args[1]) == 0) {
99 memprintf(err,
100 "'%s' expects a single character.",
101 args[0]);
102 return -1;
103 }
104 if (strlen(args[1]) > 1) {
105 memprintf(err,
106 "'%s' expects a single character, got '%s'.",
107 args[0], args[1]);
108 return -1;
109 }
110
Willy Tarreaub7a67142016-12-21 21:18:44 +0100111 global_51degrees.property_separator = *args[1];
Dragan Dosen93b38d92015-06-29 16:43:25 +0200112
113 return 0;
114}
115
Dragan Dosen105c8e62015-06-29 16:43:26 +0200116static int _51d_cache_size(char **args, int section_type, struct proxy *curpx,
117 struct proxy *defpx, const char *file, int line,
118 char **err)
119{
120 if (*(args[1]) == 0) {
121 memprintf(err,
122 "'%s' expects a positive numeric value.",
123 args[0]);
124 return -1;
125 }
126
Willy Tarreaub7a67142016-12-21 21:18:44 +0100127 global_51degrees.cache_size = atoi(args[1]);
128 if (global_51degrees.cache_size < 0) {
Dragan Dosen105c8e62015-06-29 16:43:26 +0200129 memprintf(err,
130 "'%s' expects a positive numeric value, got '%s'.",
131 args[0], args[1]);
132 return -1;
133 }
134
James Rosewella28bbd52015-09-18 18:28:52 +0100135 return 0;
136}
137
138static int _51d_fetch_check(struct arg *arg, char **err_msg)
139{
Willy Tarreaub7a67142016-12-21 21:18:44 +0100140 if (global_51degrees.data_file_path)
James Rosewella28bbd52015-09-18 18:28:52 +0100141 return 1;
142
143 memprintf(err_msg, "51Degrees data file is not specified (parameter '51degrees-data-file')");
Dragan Dosen105c8e62015-06-29 16:43:26 +0200144 return 0;
145}
146
Dragan Dosen9373fc52015-08-07 16:41:23 +0200147static int _51d_conv_check(struct arg *arg, struct sample_conv *conv,
James Rosewella28bbd52015-09-18 18:28:52 +0100148 const char *file, int line, char **err_msg)
Dragan Dosen9373fc52015-08-07 16:41:23 +0200149{
Willy Tarreaub7a67142016-12-21 21:18:44 +0100150 if (global_51degrees.data_file_path)
Dragan Dosen9373fc52015-08-07 16:41:23 +0200151 return 1;
152
James Rosewella28bbd52015-09-18 18:28:52 +0100153 memprintf(err_msg, "51Degrees data file is not specified (parameter '51degrees-data-file')");
Dragan Dosen9373fc52015-08-07 16:41:23 +0200154 return 0;
155}
156
James Rosewella28bbd52015-09-18 18:28:52 +0100157#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000158static void _51d_lru_free(void *cache_entry)
159{
Willy Tarreau83061a82018-07-13 11:56:34 +0200160 struct buffer *ptr = cache_entry;
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000161
162 if (!ptr)
163 return;
164
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200165 free(ptr->area);
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000166 free(ptr);
167}
168
169/* Allocates memory freeing space in the cache if necessary.
170*/
171static void *_51d_malloc(int size)
172{
173 void *ptr = malloc(size);
174
175 if (!ptr) {
176 /* free the oldest 10 entries from lru to free up some memory
177 * then try allocating memory again */
178 lru64_kill_oldest(_51d_lru_tree, 10);
179 ptr = malloc(size);
180 }
181
182 return ptr;
183}
184
James Rosewell63426cb2015-09-18 19:53:05 +0100185/* Insert the data associated with the sample into the cache as a fresh item.
186 */
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000187static void _51d_insert_cache_entry(struct sample *smp, struct lru64 *lru, void* domain)
James Rosewell63426cb2015-09-18 19:53:05 +0100188{
Willy Tarreau83061a82018-07-13 11:56:34 +0200189 struct buffer *cache_entry = _51d_malloc(sizeof(*cache_entry));
James Rosewell63426cb2015-09-18 19:53:05 +0100190
191 if (!cache_entry)
192 return;
193
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200194 cache_entry->area = _51d_malloc(smp->data.u.str.data + 1);
195 if (!cache_entry->area) {
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000196 free(cache_entry);
James Rosewell63426cb2015-09-18 19:53:05 +0100197 return;
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000198 }
James Rosewell63426cb2015-09-18 19:53:05 +0100199
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200200 memcpy(cache_entry->area, smp->data.u.str.area, smp->data.u.str.data);
201 cache_entry->area[smp->data.u.str.data] = 0;
202 cache_entry->data = smp->data.u.str.data;
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000203 lru64_commit(lru, cache_entry, domain, 0, _51d_lru_free);
James Rosewell63426cb2015-09-18 19:53:05 +0100204}
205
206/* Retrieves the data from the cache and sets the sample data to this string.
207 */
208static void _51d_retrieve_cache_entry(struct sample *smp, struct lru64 *lru)
209{
Willy Tarreau83061a82018-07-13 11:56:34 +0200210 struct buffer *cache_entry = lru->data;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200211 smp->data.u.str.area = cache_entry->area;
212 smp->data.u.str.data = cache_entry->data;
James Rosewell63426cb2015-09-18 19:53:05 +0100213}
214#endif
215
216#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100217/* Sets the important HTTP headers ahead of the detection
218 */
219static void _51d_set_headers(struct sample *smp, fiftyoneDegreesWorkset *ws)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200220{
James Rosewella28bbd52015-09-18 18:28:52 +0100221 struct hdr_idx *idx;
222 struct hdr_ctx ctx;
223 const struct http_msg *msg;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200224 int i;
James Rosewella28bbd52015-09-18 18:28:52 +0100225
226 idx = &smp->strm->txn->hdr_idx;
227 msg = &smp->strm->txn->req;
228
229 ws->importantHeadersCount = 0;
230
Willy Tarreaub7a67142016-12-21 21:18:44 +0100231 for (i = 0; i < global_51degrees.header_count; i++) {
James Rosewella28bbd52015-09-18 18:28:52 +0100232 ctx.idx = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200233 if (http_find_full_header2((global_51degrees.header_names + i)->area,
234 (global_51degrees.header_names + i)->data,
235 msg->chn->buf->p, idx, &ctx) == 1) {
James Rosewella28bbd52015-09-18 18:28:52 +0100236 ws->importantHeaders[ws->importantHeadersCount].header = ws->dataSet->httpHeaders + i;
237 ws->importantHeaders[ws->importantHeadersCount].headerValue = ctx.line + ctx.val;
238 ws->importantHeaders[ws->importantHeadersCount].headerValueLength = ctx.vlen;
239 ws->importantHeadersCount++;
240 }
241 }
242}
Dragan Dosen93b38d92015-06-29 16:43:25 +0200243#endif
James Rosewella28bbd52015-09-18 18:28:52 +0100244
Dragan Dosen93b38d92015-06-29 16:43:25 +0200245#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100246static void _51d_set_device_offsets(struct sample *smp)
247{
248 struct hdr_idx *idx;
249 struct hdr_ctx ctx;
250 const struct http_msg *msg;
251 int index;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100252 fiftyoneDegreesDeviceOffsets *offsets = &global_51degrees.device_offsets;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200253
James Rosewella28bbd52015-09-18 18:28:52 +0100254 idx = &smp->strm->txn->hdr_idx;
255 msg = &smp->strm->txn->req;
256 offsets->size = 0;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200257
Willy Tarreaub7a67142016-12-21 21:18:44 +0100258 for (index = 0; index < global_51degrees.header_count; index++) {
James Rosewella28bbd52015-09-18 18:28:52 +0100259 ctx.idx = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200260 if (http_find_full_header2((global_51degrees.header_names + index)->area,
261 (global_51degrees.header_names + index)->data,
262 msg->chn->buf->p, idx, &ctx) == 1) {
Willy Tarreaub7a67142016-12-21 21:18:44 +0100263 (offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + index);
264 (offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.line + ctx.val);
James Rosewella28bbd52015-09-18 18:28:52 +0100265 offsets->size++;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200266 }
267 }
James Rosewella28bbd52015-09-18 18:28:52 +0100268}
269#endif
Dragan Dosen93b38d92015-06-29 16:43:25 +0200270
271#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100272/* Provides a hash code for the important HTTP headers.
273 */
274unsigned long long _51d_req_hash(const struct arg *args, fiftyoneDegreesWorkset* ws)
275{
276 unsigned long long seed = _51d_lru_seed ^ (long)args;
277 unsigned long long hash = 0;
278 int i;
279 for(i = 0; i < ws->importantHeadersCount; i++) {
280 hash ^= ws->importantHeaders[i].header->headerNameOffset;
281 hash ^= XXH64(ws->importantHeaders[i].headerValue,
282 ws->importantHeaders[i].headerValueLength,
283 seed);
284 }
285 return hash;
286}
Dragan Dosen93b38d92015-06-29 16:43:25 +0200287#endif
288
Dragan Dosen93b38d92015-06-29 16:43:25 +0200289#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100290static void _51d_process_match(const struct arg *args, struct sample *smp, fiftyoneDegreesWorkset* ws)
291{
292 char *methodName;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200293#endif
294#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100295static void _51d_process_match(const struct arg *args, struct sample *smp)
296{
297 char valuesBuffer[1024];
Willy Tarreaub7a67142016-12-21 21:18:44 +0100298 const char **requiredProperties = fiftyoneDegreesGetRequiredPropertiesNames(&global_51degrees.data_set);
299 int requiredPropertiesCount = fiftyoneDegreesGetRequiredPropertiesCount(&global_51degrees.data_set);
300 fiftyoneDegreesDeviceOffsets *deviceOffsets = &global_51degrees.device_offsets;
James Rosewella28bbd52015-09-18 18:28:52 +0100301
Dragan Dosen93b38d92015-06-29 16:43:25 +0200302#endif
303
James Rosewella28bbd52015-09-18 18:28:52 +0100304 char no_data[] = "NoData"; /* response when no data could be found */
Willy Tarreau83061a82018-07-13 11:56:34 +0200305 struct buffer *temp = get_trash_chunk();
James Rosewella28bbd52015-09-18 18:28:52 +0100306 int j, i = 0, found;
307 const char* property_name;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200308
309 /* Loop through property names passed to the filter and fetch them from the dataset. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200310 while (args[i].data.str.area) {
Dragan Dosen93b38d92015-06-29 16:43:25 +0200311 /* Try to find request property in dataset. */
Dragan Dosen93b38d92015-06-29 16:43:25 +0200312 found = 0;
James Rosewella28bbd52015-09-18 18:28:52 +0100313#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200314 if (strcmp("Method", args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100315 switch(ws->method) {
316 case EXACT: methodName = "Exact"; break;
317 case NUMERIC: methodName = "Numeric"; break;
318 case NEAREST: methodName = "Nearest"; break;
319 case CLOSEST: methodName = "Closest"; break;
320 default:
321 case NONE: methodName = "None"; break;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200322 }
James Rosewella28bbd52015-09-18 18:28:52 +0100323 chunk_appendf(temp, "%s", methodName);
324 found = 1;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200325 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200326 else if (strcmp("Difference", args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100327 chunk_appendf(temp, "%d", ws->difference);
328 found = 1;
329 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200330 else if (strcmp("Rank", args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100331 chunk_appendf(temp, "%d", fiftyoneDegreesGetSignatureRank(ws));
332 found = 1;
333 }
334 else {
335 for (j = 0; j < ws->dataSet->requiredPropertyCount; j++) {
336 property_name = fiftyoneDegreesGetPropertyName(ws->dataSet, ws->dataSet->requiredProperties[j]);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200337 if (strcmp(property_name, args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100338 found = 1;
339 fiftyoneDegreesSetValues(ws, j);
340 chunk_appendf(temp, "%s", fiftyoneDegreesGetValueName(ws->dataSet, *ws->values));
341 break;
342 }
343 }
Dragan Dosen93b38d92015-06-29 16:43:25 +0200344 }
345#endif
346#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100347 found = 0;
348 for (j = 0; j < requiredPropertiesCount; j++) {
349 property_name = requiredProperties[j];
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200350 if (strcmp(property_name, args[i].data.str.area) == 0 &&
Willy Tarreaub7a67142016-12-21 21:18:44 +0100351 fiftyoneDegreesGetValueFromOffsets(&global_51degrees.data_set, deviceOffsets, j, valuesBuffer, 1024) > 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100352 found = 1;
353 chunk_appendf(temp, "%s", valuesBuffer);
354 break;
355 }
Dragan Dosen93b38d92015-06-29 16:43:25 +0200356 }
James Rosewella28bbd52015-09-18 18:28:52 +0100357#endif
ben@51degrees.comd3842522016-01-08 13:52:32 +0000358 if (!found)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200359 chunk_appendf(temp, "%s", no_data);
ben@51degrees.comd3842522016-01-08 13:52:32 +0000360
Dragan Dosen93b38d92015-06-29 16:43:25 +0200361 /* Add separator. */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100362 chunk_appendf(temp, "%c", global_51degrees.property_separator);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200363 ++i;
364 }
365
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200366 if (temp->data) {
367 --temp->data;
368 temp->area[temp->data] = '\0';
Dragan Dosen93b38d92015-06-29 16:43:25 +0200369 }
370
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200371 smp->data.u.str.area = temp->area;
372 smp->data.u.str.data = temp->data;
James Rosewella28bbd52015-09-18 18:28:52 +0100373}
374
375static int _51d_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
376{
377#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
378 fiftyoneDegreesWorkset* ws; /* workset for detection */
379 struct lru64 *lru = NULL;
380#endif
381
382 /* Needed to ensure that the HTTP message has been fully recieved when
383 * used with TCP operation. Not required for HTTP operation.
384 * Data type has to be reset to ensure the string output is processed
385 * correctly.
386 */
387 CHECK_HTTP_MESSAGE_FIRST();
388 smp->data.type = SMP_T_STR;
389
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000390 /* Flags the sample to show it uses constant memory*/
391 smp->flags |= SMP_F_CONST;
392
James Rosewella28bbd52015-09-18 18:28:52 +0100393#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
394
395 /* Get only the headers needed for device detection so they can be used
396 * with the cache to return previous results. Pattern is slower than
397 * Trie so caching will help improve performance.
398 */
399
400 /* Get a workset from the pool which will later contain detection results. */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100401 ws = fiftyoneDegreesWorksetPoolGet(global_51degrees.pool);
James Rosewella28bbd52015-09-18 18:28:52 +0100402 if (!ws)
403 return 0;
404
405 /* Set the important HTTP headers for this request in the workset. */
406 _51d_set_headers(smp, ws);
407
408 /* Check the cache to see if there's results for these headers already. */
409 if (_51d_lru_tree) {
410 lru = lru64_get(_51d_req_hash(args, ws),
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000411 _51d_lru_tree, (void*)args, 0);
James Rosewella28bbd52015-09-18 18:28:52 +0100412 if (lru && lru->domain) {
Willy Tarreaub7a67142016-12-21 21:18:44 +0100413 fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws);
James Rosewell63426cb2015-09-18 19:53:05 +0100414 _51d_retrieve_cache_entry(smp, lru);
James Rosewella28bbd52015-09-18 18:28:52 +0100415 return 1;
416 }
417 }
418
419 fiftyoneDegreesMatchForHttpHeaders(ws);
420
421 _51d_process_match(args, smp, ws);
422
423#endif
424
425#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
426
427 /* Trie is very fast so all the headers can be passed in and the result
428 * returned faster than the hashing algorithm process.
429 */
430 _51d_set_device_offsets(smp);
431 _51d_process_match(args, smp);
432
433#endif
434
435#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100436 fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws);
ben@51degrees.comd3842522016-01-08 13:52:32 +0000437 if (lru)
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000438 _51d_insert_cache_entry(smp, lru, (void*)args);
James Rosewella28bbd52015-09-18 18:28:52 +0100439#endif
440
441 return 1;
442}
Dragan Dosen93b38d92015-06-29 16:43:25 +0200443
James Rosewella28bbd52015-09-18 18:28:52 +0100444static int _51d_conv(const struct arg *args, struct sample *smp, void *private)
445{
Dragan Dosen93b38d92015-06-29 16:43:25 +0200446#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100447 fiftyoneDegreesWorkset* ws; /* workset for detection */
448 struct lru64 *lru = NULL;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200449#endif
450
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000451 /* Flags the sample to show it uses constant memory*/
452 smp->flags |= SMP_F_CONST;
453
James Rosewella28bbd52015-09-18 18:28:52 +0100454#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
455
456 /* Look in the list. */
457 if (_51d_lru_tree) {
458 unsigned long long seed = _51d_lru_seed ^ (long)args;
459
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200460 lru = lru64_get(XXH64(smp->data.u.str.area, smp->data.u.str.data, seed),
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000461 _51d_lru_tree, (void*)args, 0);
James Rosewella28bbd52015-09-18 18:28:52 +0100462 if (lru && lru->domain) {
James Rosewell63426cb2015-09-18 19:53:05 +0100463 _51d_retrieve_cache_entry(smp, lru);
James Rosewella28bbd52015-09-18 18:28:52 +0100464 return 1;
465 }
466 }
467
468 /* Create workset. This will later contain detection results. */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100469 ws = fiftyoneDegreesWorksetPoolGet(global_51degrees.pool);
James Rosewella28bbd52015-09-18 18:28:52 +0100470 if (!ws)
471 return 0;
472#endif
473
474 /* Duplicate the data and remove the "const" flag before device detection. */
475 if (!smp_dup(smp))
476 return 0;
477
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200478 smp->data.u.str.area[smp->data.u.str.data] = '\0';
James Rosewella28bbd52015-09-18 18:28:52 +0100479
480 /* Perform detection. */
481#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200482 fiftyoneDegreesMatch(ws, smp->data.u.str.area);
James Rosewella28bbd52015-09-18 18:28:52 +0100483 _51d_process_match(args, smp, ws);
484#endif
485#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200486 global_51degrees.device_offsets.firstOffset->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set,
487 smp->data.u.str.area);
Willy Tarreaub7a67142016-12-21 21:18:44 +0100488 global_51degrees.device_offsets.size = 1;
James Rosewella28bbd52015-09-18 18:28:52 +0100489 _51d_process_match(args, smp);
490#endif
491
492#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100493 fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws);
ben@51degrees.comd3842522016-01-08 13:52:32 +0000494 if (lru)
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000495 _51d_insert_cache_entry(smp, lru, (void*)args);
James Rosewella28bbd52015-09-18 18:28:52 +0100496#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +0200497
Dragan Dosen93b38d92015-06-29 16:43:25 +0200498 return 1;
499}
500
James Rosewella28bbd52015-09-18 18:28:52 +0100501#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
502void _51d_init_http_headers()
503{
504 int index = 0;
505 const fiftyoneDegreesAsciiString *headerName;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100506 fiftyoneDegreesDataSet *ds = &global_51degrees.data_set;
507 global_51degrees.header_count = ds->httpHeadersCount;
Willy Tarreau83061a82018-07-13 11:56:34 +0200508 global_51degrees.header_names = malloc(global_51degrees.header_count * sizeof(struct buffer));
Willy Tarreaub7a67142016-12-21 21:18:44 +0100509 for (index = 0; index < global_51degrees.header_count; index++) {
James Rosewella28bbd52015-09-18 18:28:52 +0100510 headerName = fiftyoneDegreesGetString(ds, ds->httpHeaders[index].headerNameOffset);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200511 (global_51degrees.header_names + index)->area = (char*)&headerName->firstByte;
512 (global_51degrees.header_names + index)->data = headerName->length - 1;
513 (global_51degrees.header_names + index)->size = (global_51degrees.header_names + index)->data;
James Rosewella28bbd52015-09-18 18:28:52 +0100514 }
515}
516#endif
517
518#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
519void _51d_init_http_headers()
520{
521 int index = 0;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100522 fiftyoneDegreesDataSet *ds = &global_51degrees.data_set;
523 global_51degrees.header_count = fiftyoneDegreesGetHttpHeaderCount(ds);
524 global_51degrees.device_offsets.firstOffset = malloc(
525 global_51degrees.header_count * sizeof(fiftyoneDegreesDeviceOffset));
Willy Tarreau83061a82018-07-13 11:56:34 +0200526 global_51degrees.header_names = malloc(global_51degrees.header_count * sizeof(struct buffer));
Willy Tarreaub7a67142016-12-21 21:18:44 +0100527 global_51degrees.header_offsets = malloc(global_51degrees.header_count * sizeof(int32_t));
528 for (index = 0; index < global_51degrees.header_count; index++) {
529 global_51degrees.header_offsets[index] = fiftyoneDegreesGetHttpHeaderNameOffset(ds, index);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200530 global_51degrees.header_names->area = (char*)fiftyoneDegreesGetHttpHeaderNamePointer(ds, index);
531 global_51degrees.header_names->data = strlen(global_51degrees.header_names->area);
532 global_51degrees.header_names[index].size = global_51degrees.header_names->data;
James Rosewella28bbd52015-09-18 18:28:52 +0100533 }
534}
535#endif
536
Willy Tarreau9f3f2542016-12-21 20:30:05 +0100537/*
538 * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
539 */
540static int init_51degrees(void)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200541{
542 int i = 0;
Willy Tarreau83061a82018-07-13 11:56:34 +0200543 struct buffer *temp;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200544 struct _51d_property_names *name;
545 char **_51d_property_list = NULL;
546 fiftyoneDegreesDataSetInitStatus _51d_dataset_status = DATA_SET_INIT_STATUS_NOT_SET;
547
Willy Tarreaub7a67142016-12-21 21:18:44 +0100548 if (!global_51degrees.data_file_path)
Willy Tarreau9f3f2542016-12-21 20:30:05 +0100549 return 0;
Dragan Dosen9373fc52015-08-07 16:41:23 +0200550
Christopher Faulete8ca4342017-10-25 17:23:02 +0200551 if (global.nbthread > 1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100552 ha_alert("51Degrees: multithreading is not supported for now.\n");
Christopher Faulete8ca4342017-10-25 17:23:02 +0200553 return (ERR_FATAL | ERR_ALERT);
554 }
555
Willy Tarreaub7a67142016-12-21 21:18:44 +0100556 if (!LIST_ISEMPTY(&global_51degrees.property_names)) {
Dragan Dosen93b38d92015-06-29 16:43:25 +0200557 i = 0;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100558 list_for_each_entry(name, &global_51degrees.property_names, list)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200559 ++i;
560 _51d_property_list = calloc(i, sizeof(char *));
561
562 i = 0;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100563 list_for_each_entry(name, &global_51degrees.property_names, list)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200564 _51d_property_list[i++] = name->name;
565 }
566
Willy Tarreaub7a67142016-12-21 21:18:44 +0100567 _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global_51degrees.data_file_path, &global_51degrees.data_set, (const char**)_51d_property_list, i);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200568
569 temp = get_trash_chunk();
570 chunk_reset(temp);
571
572 switch (_51d_dataset_status) {
573 case DATA_SET_INIT_STATUS_SUCCESS:
James Rosewella28bbd52015-09-18 18:28:52 +0100574#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
575 /* only 1 workset in the pool because HAProxy is currently single threaded
576 * this value should be set to the number of threads in future versions.
577 */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100578 global_51degrees.pool = fiftyoneDegreesWorksetPoolCreate(&global_51degrees.data_set, NULL, 1);
James Rosewella28bbd52015-09-18 18:28:52 +0100579#endif
580 _51d_init_http_headers();
Dragan Dosen93b38d92015-06-29 16:43:25 +0200581 break;
582 case DATA_SET_INIT_STATUS_INSUFFICIENT_MEMORY:
583 chunk_printf(temp, "Insufficient memory.");
584 break;
585 case DATA_SET_INIT_STATUS_CORRUPT_DATA:
586#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
587 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Pattern data format.");
588#endif
589#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
590 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Trie data format.");
591#endif
592 break;
593 case DATA_SET_INIT_STATUS_INCORRECT_VERSION:
594#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
595 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Pattern data format.");
596#endif
597#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
598 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Trie data format.");
599#endif
600 break;
601 case DATA_SET_INIT_STATUS_FILE_NOT_FOUND:
602 chunk_printf(temp, "File not found.");
603 break;
ben51degrees1f077eb2016-07-06 12:07:21 +0100604 case DATA_SET_INIT_STATUS_NULL_POINTER:
605 chunk_printf(temp, "Null pointer to the existing dataset or memory location.");
606 break;
607 case DATA_SET_INIT_STATUS_POINTER_OUT_OF_BOUNDS:
608 chunk_printf(temp, "Allocated continuous memory containing 51Degrees data file appears to be smaller than expected. Most likely"
609 " because the data file was not fully loaded into the allocated memory.");
610 break;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200611 case DATA_SET_INIT_STATUS_NOT_SET:
612 chunk_printf(temp, "Data set not initialised.");
613 break;
Dragan Dosen483b93c2017-09-27 12:46:44 +0200614 default:
615 chunk_printf(temp, "Other error.");
616 break;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200617 }
618 if (_51d_dataset_status != DATA_SET_INIT_STATUS_SUCCESS) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200619 if (temp->data)
620 ha_alert("51Degrees Setup - Error reading 51Degrees data file. %s\n",
621 temp->area);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200622 else
Christopher Faulet767a84b2017-11-24 16:50:31 +0100623 ha_alert("51Degrees Setup - Error reading 51Degrees data file.\n");
Willy Tarreau9f3f2542016-12-21 20:30:05 +0100624 return ERR_ALERT | ERR_FATAL;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200625 }
626 free(_51d_property_list);
627
James Rosewella28bbd52015-09-18 18:28:52 +0100628#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Dragan Dosen105c8e62015-06-29 16:43:26 +0200629 _51d_lru_seed = random();
Willy Tarreaub7a67142016-12-21 21:18:44 +0100630 if (global_51degrees.cache_size) {
631 _51d_lru_tree = lru64_new(global_51degrees.cache_size);
James Rosewella28bbd52015-09-18 18:28:52 +0100632 }
633#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +0200634
Dragan Dosen93b38d92015-06-29 16:43:25 +0200635 return 0;
636}
637
Willy Tarreau7ac4c202016-12-21 20:59:01 +0100638static void deinit_51degrees(void)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200639{
640 struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
641
Willy Tarreaub7a67142016-12-21 21:18:44 +0100642 free(global_51degrees.header_names);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200643#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100644 fiftyoneDegreesWorksetPoolFree(global_51degrees.pool);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200645#endif
646#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100647 free(global_51degrees.device_offsets.firstOffset);
648 free(global_51degrees.header_offsets);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200649#endif
Willy Tarreaub7a67142016-12-21 21:18:44 +0100650 fiftyoneDegreesDataSetFree(&global_51degrees.data_set);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200651
Willy Tarreaub7a67142016-12-21 21:18:44 +0100652 free(global_51degrees.data_file_path); global_51degrees.data_file_path = NULL;
653 list_for_each_entry_safe(_51d_prop_name, _51d_prop_nameb, &global_51degrees.property_names, list) {
Dragan Dosen93b38d92015-06-29 16:43:25 +0200654 LIST_DEL(&_51d_prop_name->list);
655 free(_51d_prop_name);
656 }
Dragan Dosen105c8e62015-06-29 16:43:26 +0200657
James Rosewella28bbd52015-09-18 18:28:52 +0100658#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Dragan Dosen105c8e62015-06-29 16:43:26 +0200659 while (lru64_destroy(_51d_lru_tree));
James Rosewella28bbd52015-09-18 18:28:52 +0100660#endif
Dragan Dosen93b38d92015-06-29 16:43:25 +0200661}
662
663static struct cfg_kw_list _51dcfg_kws = {{ }, {
664 { CFG_GLOBAL, "51degrees-data-file", _51d_data_file },
665 { CFG_GLOBAL, "51degrees-property-name-list", _51d_property_name_list },
666 { CFG_GLOBAL, "51degrees-property-separator", _51d_property_separator },
Dragan Dosen105c8e62015-06-29 16:43:26 +0200667 { CFG_GLOBAL, "51degrees-cache-size", _51d_cache_size },
Dragan Dosen93b38d92015-06-29 16:43:25 +0200668 { 0, NULL, NULL },
669}};
670
Willy Tarreau0108d902018-11-25 19:14:37 +0100671INITCALL1(STG_REGISTER, cfg_register_keywords, &_51dcfg_kws);
672
Dragan Dosen93b38d92015-06-29 16:43:25 +0200673/* Note: must not be declared <const> as its list will be overwritten */
James Rosewella28bbd52015-09-18 18:28:52 +0100674static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
675 { "51d.all", _51d_fetch, ARG5(1,STR,STR,STR,STR,STR), _51d_fetch_check, SMP_T_STR, SMP_USE_HRQHV },
676 { NULL, NULL, 0, 0, 0 },
677}};
678
Willy Tarreau0108d902018-11-25 19:14:37 +0100679INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
680
James Rosewella28bbd52015-09-18 18:28:52 +0100681/* Note: must not be declared <const> as its list will be overwritten */
Dragan Dosen93b38d92015-06-29 16:43:25 +0200682static struct sample_conv_kw_list conv_kws = {ILH, {
James Rosewella28bbd52015-09-18 18:28:52 +0100683 { "51d.single", _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 +0200684 { NULL, NULL, 0, 0, 0 },
685}};
686
Willy Tarreau0108d902018-11-25 19:14:37 +0100687INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
688
Dragan Dosen93b38d92015-06-29 16:43:25 +0200689__attribute__((constructor))
690static void __51d_init(void)
691{
James Rosewella28bbd52015-09-18 18:28:52 +0100692 /* register sample fetch and conversion keywords */
Willy Tarreaub5e58d62016-12-21 18:49:29 +0100693 hap_register_build_opts("Built with 51Degrees support.", 0);
Willy Tarreau9f3f2542016-12-21 20:30:05 +0100694 hap_register_post_check(init_51degrees);
Willy Tarreau7ac4c202016-12-21 20:59:01 +0100695 hap_register_post_deinit(deinit_51degrees);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200696}