blob: 20760d639ef81f87f2d48901c45638696b653541 [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>
Willy Tarreau80713382018-11-26 10:19:54 +01008#include <types/global.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +02009#include <proto/arg.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020010#include <proto/http_fetch.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +020011#include <proto/log.h>
James Rosewella28bbd52015-09-18 18:28:52 +010012#include <proto/proto_http.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +020013#include <proto/sample.h>
14#include <import/xxhash.h>
Dragan Dosen105c8e62015-06-29 16:43:26 +020015#include <import/lru.h>
Willy Tarreaub7a67142016-12-21 21:18:44 +010016#include <51Degrees.h>
Dragan Dosen93b38d92015-06-29 16:43:25 +020017
18struct _51d_property_names {
19 struct list list;
20 char *name;
21};
22
James Rosewella28bbd52015-09-18 18:28:52 +010023#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Dragan Dosen105c8e62015-06-29 16:43:26 +020024static struct lru64_head *_51d_lru_tree = NULL;
25static unsigned long long _51d_lru_seed;
James Rosewella28bbd52015-09-18 18:28:52 +010026#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +020027
Willy Tarreaub7a67142016-12-21 21:18:44 +010028static struct {
29 char property_separator; /* the separator to use in the response for the values. this is taken from 51degrees-property-separator from config. */
30 struct list property_names; /* list of properties to load into the data set. this is taken from 51degrees-property-name-list from config. */
31 char *data_file_path;
32 int header_count; /* number of HTTP headers related to device detection. */
Willy Tarreau83061a82018-07-13 11:56:34 +020033 struct buffer *header_names; /* array of HTTP header names. */
Willy Tarreaub7a67142016-12-21 21:18:44 +010034 fiftyoneDegreesDataSet data_set; /* data set used with the pattern and trie detection methods. */
35#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
36 fiftyoneDegreesWorksetPool *pool; /* pool of worksets to avoid creating a new one for each request. */
37#endif
38#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
39 int32_t *header_offsets; /* offsets to the HTTP header name string. */
40 fiftyoneDegreesDeviceOffsets device_offsets; /* Memory used for device offsets. */
41#endif
42 int cache_size;
43} global_51degrees = {
44 .property_separator = ',',
45 .property_names = LIST_HEAD_INIT(global_51degrees.property_names),
46 .data_file_path = NULL,
47#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
48 .data_set = { },
49#endif
50 .cache_size = 0,
51};
52
Dragan Dosen93b38d92015-06-29 16:43:25 +020053static int _51d_data_file(char **args, int section_type, struct proxy *curpx,
54 struct proxy *defpx, const char *file, int line,
55 char **err)
56{
57 if (*(args[1]) == 0) {
58 memprintf(err,
59 "'%s' expects a filepath to a 51Degrees trie or pattern data file.",
60 args[0]);
61 return -1;
62 }
63
Willy Tarreaub7a67142016-12-21 21:18:44 +010064 if (global_51degrees.data_file_path)
65 free(global_51degrees.data_file_path);
66 global_51degrees.data_file_path = strdup(args[1]);
Dragan Dosen93b38d92015-06-29 16:43:25 +020067
68 return 0;
69}
70
71static int _51d_property_name_list(char **args, int section_type, struct proxy *curpx,
James Rosewella28bbd52015-09-18 18:28:52 +010072 struct proxy *defpx, const char *file, int line,
73 char **err)
Dragan Dosen93b38d92015-06-29 16:43:25 +020074{
75 int cur_arg = 1;
76 struct _51d_property_names *name;
77
78 if (*(args[cur_arg]) == 0) {
79 memprintf(err,
80 "'%s' expects at least one 51Degrees property name.",
81 args[0]);
82 return -1;
83 }
84
85 while (*(args[cur_arg])) {
Vincent Bernat02779b62016-04-03 13:48:43 +020086 name = calloc(1, sizeof(*name));
Dragan Dosen93b38d92015-06-29 16:43:25 +020087 name->name = strdup(args[cur_arg]);
Willy Tarreaub7a67142016-12-21 21:18:44 +010088 LIST_ADDQ(&global_51degrees.property_names, &name->list);
Dragan Dosen93b38d92015-06-29 16:43:25 +020089 ++cur_arg;
90 }
91
92 return 0;
93}
94
95static int _51d_property_separator(char **args, int section_type, struct proxy *curpx,
96 struct proxy *defpx, const char *file, int line,
97 char **err)
98{
99 if (*(args[1]) == 0) {
100 memprintf(err,
101 "'%s' expects a single character.",
102 args[0]);
103 return -1;
104 }
105 if (strlen(args[1]) > 1) {
106 memprintf(err,
107 "'%s' expects a single character, got '%s'.",
108 args[0], args[1]);
109 return -1;
110 }
111
Willy Tarreaub7a67142016-12-21 21:18:44 +0100112 global_51degrees.property_separator = *args[1];
Dragan Dosen93b38d92015-06-29 16:43:25 +0200113
114 return 0;
115}
116
Dragan Dosen105c8e62015-06-29 16:43:26 +0200117static int _51d_cache_size(char **args, int section_type, struct proxy *curpx,
118 struct proxy *defpx, const char *file, int line,
119 char **err)
120{
121 if (*(args[1]) == 0) {
122 memprintf(err,
123 "'%s' expects a positive numeric value.",
124 args[0]);
125 return -1;
126 }
127
Willy Tarreaub7a67142016-12-21 21:18:44 +0100128 global_51degrees.cache_size = atoi(args[1]);
129 if (global_51degrees.cache_size < 0) {
Dragan Dosen105c8e62015-06-29 16:43:26 +0200130 memprintf(err,
131 "'%s' expects a positive numeric value, got '%s'.",
132 args[0], args[1]);
133 return -1;
134 }
135
James Rosewella28bbd52015-09-18 18:28:52 +0100136 return 0;
137}
138
139static int _51d_fetch_check(struct arg *arg, char **err_msg)
140{
Willy Tarreaub7a67142016-12-21 21:18:44 +0100141 if (global_51degrees.data_file_path)
James Rosewella28bbd52015-09-18 18:28:52 +0100142 return 1;
143
144 memprintf(err_msg, "51Degrees data file is not specified (parameter '51degrees-data-file')");
Dragan Dosen105c8e62015-06-29 16:43:26 +0200145 return 0;
146}
147
Dragan Dosen9373fc52015-08-07 16:41:23 +0200148static int _51d_conv_check(struct arg *arg, struct sample_conv *conv,
James Rosewella28bbd52015-09-18 18:28:52 +0100149 const char *file, int line, char **err_msg)
Dragan Dosen9373fc52015-08-07 16:41:23 +0200150{
Willy Tarreaub7a67142016-12-21 21:18:44 +0100151 if (global_51degrees.data_file_path)
Dragan Dosen9373fc52015-08-07 16:41:23 +0200152 return 1;
153
James Rosewella28bbd52015-09-18 18:28:52 +0100154 memprintf(err_msg, "51Degrees data file is not specified (parameter '51degrees-data-file')");
Dragan Dosen9373fc52015-08-07 16:41:23 +0200155 return 0;
156}
157
James Rosewella28bbd52015-09-18 18:28:52 +0100158#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000159static void _51d_lru_free(void *cache_entry)
160{
Willy Tarreau83061a82018-07-13 11:56:34 +0200161 struct buffer *ptr = cache_entry;
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000162
163 if (!ptr)
164 return;
165
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200166 free(ptr->area);
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000167 free(ptr);
168}
169
170/* Allocates memory freeing space in the cache if necessary.
171*/
172static void *_51d_malloc(int size)
173{
174 void *ptr = malloc(size);
175
176 if (!ptr) {
177 /* free the oldest 10 entries from lru to free up some memory
178 * then try allocating memory again */
179 lru64_kill_oldest(_51d_lru_tree, 10);
180 ptr = malloc(size);
181 }
182
183 return ptr;
184}
185
James Rosewell63426cb2015-09-18 19:53:05 +0100186/* Insert the data associated with the sample into the cache as a fresh item.
187 */
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000188static void _51d_insert_cache_entry(struct sample *smp, struct lru64 *lru, void* domain)
James Rosewell63426cb2015-09-18 19:53:05 +0100189{
Willy Tarreau83061a82018-07-13 11:56:34 +0200190 struct buffer *cache_entry = _51d_malloc(sizeof(*cache_entry));
James Rosewell63426cb2015-09-18 19:53:05 +0100191
192 if (!cache_entry)
193 return;
194
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200195 cache_entry->area = _51d_malloc(smp->data.u.str.data + 1);
196 if (!cache_entry->area) {
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000197 free(cache_entry);
James Rosewell63426cb2015-09-18 19:53:05 +0100198 return;
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000199 }
James Rosewell63426cb2015-09-18 19:53:05 +0100200
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200201 memcpy(cache_entry->area, smp->data.u.str.area, smp->data.u.str.data);
202 cache_entry->area[smp->data.u.str.data] = 0;
203 cache_entry->data = smp->data.u.str.data;
ben@51degrees.comc9dfa242016-01-08 13:47:46 +0000204 lru64_commit(lru, cache_entry, domain, 0, _51d_lru_free);
James Rosewell63426cb2015-09-18 19:53:05 +0100205}
206
207/* Retrieves the data from the cache and sets the sample data to this string.
208 */
209static void _51d_retrieve_cache_entry(struct sample *smp, struct lru64 *lru)
210{
Willy Tarreau83061a82018-07-13 11:56:34 +0200211 struct buffer *cache_entry = lru->data;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200212 smp->data.u.str.area = cache_entry->area;
213 smp->data.u.str.data = cache_entry->data;
James Rosewell63426cb2015-09-18 19:53:05 +0100214}
215#endif
216
217#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100218/* Sets the important HTTP headers ahead of the detection
219 */
220static void _51d_set_headers(struct sample *smp, fiftyoneDegreesWorkset *ws)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200221{
James Rosewella28bbd52015-09-18 18:28:52 +0100222 struct hdr_idx *idx;
223 struct hdr_ctx ctx;
224 const struct http_msg *msg;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200225 int i;
James Rosewella28bbd52015-09-18 18:28:52 +0100226
227 idx = &smp->strm->txn->hdr_idx;
228 msg = &smp->strm->txn->req;
229
230 ws->importantHeadersCount = 0;
231
Willy Tarreaub7a67142016-12-21 21:18:44 +0100232 for (i = 0; i < global_51degrees.header_count; i++) {
James Rosewella28bbd52015-09-18 18:28:52 +0100233 ctx.idx = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200234 if (http_find_full_header2((global_51degrees.header_names + i)->area,
235 (global_51degrees.header_names + i)->data,
Ben51Degreesdaa356b2019-01-16 10:19:15 +0000236#ifndef BUF_NULL
237 msg->chn->buf->p,
238#else
239 ci_head(msg->chn),
240#endif
241 idx,
242 &ctx) == 1) {
James Rosewella28bbd52015-09-18 18:28:52 +0100243 ws->importantHeaders[ws->importantHeadersCount].header = ws->dataSet->httpHeaders + i;
244 ws->importantHeaders[ws->importantHeadersCount].headerValue = ctx.line + ctx.val;
245 ws->importantHeaders[ws->importantHeadersCount].headerValueLength = ctx.vlen;
246 ws->importantHeadersCount++;
247 }
248 }
249}
Dragan Dosen93b38d92015-06-29 16:43:25 +0200250#endif
James Rosewella28bbd52015-09-18 18:28:52 +0100251
Dragan Dosen93b38d92015-06-29 16:43:25 +0200252#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100253static void _51d_set_device_offsets(struct sample *smp)
254{
255 struct hdr_idx *idx;
256 struct hdr_ctx ctx;
257 const struct http_msg *msg;
258 int index;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100259 fiftyoneDegreesDeviceOffsets *offsets = &global_51degrees.device_offsets;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200260
James Rosewella28bbd52015-09-18 18:28:52 +0100261 idx = &smp->strm->txn->hdr_idx;
262 msg = &smp->strm->txn->req;
263 offsets->size = 0;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200264
Willy Tarreaub7a67142016-12-21 21:18:44 +0100265 for (index = 0; index < global_51degrees.header_count; index++) {
James Rosewella28bbd52015-09-18 18:28:52 +0100266 ctx.idx = 0;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200267 if (http_find_full_header2((global_51degrees.header_names + index)->area,
268 (global_51degrees.header_names + index)->data,
Ben51Degreesdaa356b2019-01-16 10:19:15 +0000269#ifndef BUF_NULL
270 msg->chn->buf->p,
271#else
272 ci_head(msg->chn),
273#endif
274 idx,
275 &ctx) == 1) {
276
Willy Tarreaub7a67142016-12-21 21:18:44 +0100277 (offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + index);
278 (offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.line + ctx.val);
James Rosewella28bbd52015-09-18 18:28:52 +0100279 offsets->size++;
Dragan Dosen105c8e62015-06-29 16:43:26 +0200280 }
281 }
James Rosewella28bbd52015-09-18 18:28:52 +0100282}
283#endif
Dragan Dosen93b38d92015-06-29 16:43:25 +0200284
285#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100286/* Provides a hash code for the important HTTP headers.
287 */
288unsigned long long _51d_req_hash(const struct arg *args, fiftyoneDegreesWorkset* ws)
289{
290 unsigned long long seed = _51d_lru_seed ^ (long)args;
291 unsigned long long hash = 0;
292 int i;
293 for(i = 0; i < ws->importantHeadersCount; i++) {
294 hash ^= ws->importantHeaders[i].header->headerNameOffset;
295 hash ^= XXH64(ws->importantHeaders[i].headerValue,
296 ws->importantHeaders[i].headerValueLength,
297 seed);
298 }
299 return hash;
300}
Dragan Dosen93b38d92015-06-29 16:43:25 +0200301#endif
302
Dragan Dosen93b38d92015-06-29 16:43:25 +0200303#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100304static void _51d_process_match(const struct arg *args, struct sample *smp, fiftyoneDegreesWorkset* ws)
305{
306 char *methodName;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200307#endif
308#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100309static void _51d_process_match(const struct arg *args, struct sample *smp)
310{
311 char valuesBuffer[1024];
Willy Tarreaub7a67142016-12-21 21:18:44 +0100312 const char **requiredProperties = fiftyoneDegreesGetRequiredPropertiesNames(&global_51degrees.data_set);
313 int requiredPropertiesCount = fiftyoneDegreesGetRequiredPropertiesCount(&global_51degrees.data_set);
314 fiftyoneDegreesDeviceOffsets *deviceOffsets = &global_51degrees.device_offsets;
James Rosewella28bbd52015-09-18 18:28:52 +0100315
Dragan Dosen93b38d92015-06-29 16:43:25 +0200316#endif
317
James Rosewella28bbd52015-09-18 18:28:52 +0100318 char no_data[] = "NoData"; /* response when no data could be found */
Willy Tarreau83061a82018-07-13 11:56:34 +0200319 struct buffer *temp = get_trash_chunk();
James Rosewella28bbd52015-09-18 18:28:52 +0100320 int j, i = 0, found;
321 const char* property_name;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200322
323 /* Loop through property names passed to the filter and fetch them from the dataset. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200324 while (args[i].data.str.area) {
Dragan Dosen93b38d92015-06-29 16:43:25 +0200325 /* Try to find request property in dataset. */
Dragan Dosen93b38d92015-06-29 16:43:25 +0200326 found = 0;
James Rosewella28bbd52015-09-18 18:28:52 +0100327#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200328 if (strcmp("Method", args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100329 switch(ws->method) {
330 case EXACT: methodName = "Exact"; break;
331 case NUMERIC: methodName = "Numeric"; break;
332 case NEAREST: methodName = "Nearest"; break;
333 case CLOSEST: methodName = "Closest"; break;
334 default:
335 case NONE: methodName = "None"; break;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200336 }
James Rosewella28bbd52015-09-18 18:28:52 +0100337 chunk_appendf(temp, "%s", methodName);
338 found = 1;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200339 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200340 else if (strcmp("Difference", args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100341 chunk_appendf(temp, "%d", ws->difference);
342 found = 1;
343 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200344 else if (strcmp("Rank", args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100345 chunk_appendf(temp, "%d", fiftyoneDegreesGetSignatureRank(ws));
346 found = 1;
347 }
348 else {
349 for (j = 0; j < ws->dataSet->requiredPropertyCount; j++) {
350 property_name = fiftyoneDegreesGetPropertyName(ws->dataSet, ws->dataSet->requiredProperties[j]);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200351 if (strcmp(property_name, args[i].data.str.area) == 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100352 found = 1;
353 fiftyoneDegreesSetValues(ws, j);
354 chunk_appendf(temp, "%s", fiftyoneDegreesGetValueName(ws->dataSet, *ws->values));
355 break;
356 }
357 }
Dragan Dosen93b38d92015-06-29 16:43:25 +0200358 }
359#endif
360#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100361 found = 0;
362 for (j = 0; j < requiredPropertiesCount; j++) {
363 property_name = requiredProperties[j];
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200364 if (strcmp(property_name, args[i].data.str.area) == 0 &&
Willy Tarreaub7a67142016-12-21 21:18:44 +0100365 fiftyoneDegreesGetValueFromOffsets(&global_51degrees.data_set, deviceOffsets, j, valuesBuffer, 1024) > 0) {
James Rosewella28bbd52015-09-18 18:28:52 +0100366 found = 1;
367 chunk_appendf(temp, "%s", valuesBuffer);
368 break;
369 }
Dragan Dosen93b38d92015-06-29 16:43:25 +0200370 }
James Rosewella28bbd52015-09-18 18:28:52 +0100371#endif
ben@51degrees.comd3842522016-01-08 13:52:32 +0000372 if (!found)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200373 chunk_appendf(temp, "%s", no_data);
ben@51degrees.comd3842522016-01-08 13:52:32 +0000374
Dragan Dosen93b38d92015-06-29 16:43:25 +0200375 /* Add separator. */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100376 chunk_appendf(temp, "%c", global_51degrees.property_separator);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200377 ++i;
378 }
379
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200380 if (temp->data) {
381 --temp->data;
382 temp->area[temp->data] = '\0';
Dragan Dosen93b38d92015-06-29 16:43:25 +0200383 }
384
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200385 smp->data.u.str.area = temp->area;
386 smp->data.u.str.data = temp->data;
James Rosewella28bbd52015-09-18 18:28:52 +0100387}
388
389static int _51d_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
390{
391#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
392 fiftyoneDegreesWorkset* ws; /* workset for detection */
393 struct lru64 *lru = NULL;
394#endif
395
Joseph Herlant31019ee2018-11-25 13:15:13 -0800396 /* Needed to ensure that the HTTP message has been fully received when
James Rosewella28bbd52015-09-18 18:28:52 +0100397 * used with TCP operation. Not required for HTTP operation.
398 * Data type has to be reset to ensure the string output is processed
399 * correctly.
400 */
401 CHECK_HTTP_MESSAGE_FIRST();
402 smp->data.type = SMP_T_STR;
403
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000404 /* Flags the sample to show it uses constant memory*/
405 smp->flags |= SMP_F_CONST;
406
James Rosewella28bbd52015-09-18 18:28:52 +0100407#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
408
409 /* Get only the headers needed for device detection so they can be used
410 * with the cache to return previous results. Pattern is slower than
411 * Trie so caching will help improve performance.
412 */
413
414 /* Get a workset from the pool which will later contain detection results. */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100415 ws = fiftyoneDegreesWorksetPoolGet(global_51degrees.pool);
James Rosewella28bbd52015-09-18 18:28:52 +0100416 if (!ws)
417 return 0;
418
419 /* Set the important HTTP headers for this request in the workset. */
420 _51d_set_headers(smp, ws);
421
422 /* Check the cache to see if there's results for these headers already. */
423 if (_51d_lru_tree) {
424 lru = lru64_get(_51d_req_hash(args, ws),
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000425 _51d_lru_tree, (void*)args, 0);
James Rosewella28bbd52015-09-18 18:28:52 +0100426 if (lru && lru->domain) {
Willy Tarreaub7a67142016-12-21 21:18:44 +0100427 fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws);
James Rosewell63426cb2015-09-18 19:53:05 +0100428 _51d_retrieve_cache_entry(smp, lru);
James Rosewella28bbd52015-09-18 18:28:52 +0100429 return 1;
430 }
431 }
432
433 fiftyoneDegreesMatchForHttpHeaders(ws);
434
435 _51d_process_match(args, smp, ws);
436
437#endif
438
439#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
440
441 /* Trie is very fast so all the headers can be passed in and the result
442 * returned faster than the hashing algorithm process.
443 */
444 _51d_set_device_offsets(smp);
445 _51d_process_match(args, smp);
446
447#endif
448
449#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100450 fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws);
ben@51degrees.comd3842522016-01-08 13:52:32 +0000451 if (lru)
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000452 _51d_insert_cache_entry(smp, lru, (void*)args);
James Rosewella28bbd52015-09-18 18:28:52 +0100453#endif
454
455 return 1;
456}
Dragan Dosen93b38d92015-06-29 16:43:25 +0200457
James Rosewella28bbd52015-09-18 18:28:52 +0100458static int _51d_conv(const struct arg *args, struct sample *smp, void *private)
459{
Dragan Dosen93b38d92015-06-29 16:43:25 +0200460#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
James Rosewella28bbd52015-09-18 18:28:52 +0100461 fiftyoneDegreesWorkset* ws; /* workset for detection */
462 struct lru64 *lru = NULL;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200463#endif
464
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000465 /* Flags the sample to show it uses constant memory*/
466 smp->flags |= SMP_F_CONST;
467
James Rosewella28bbd52015-09-18 18:28:52 +0100468#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
469
470 /* Look in the list. */
471 if (_51d_lru_tree) {
472 unsigned long long seed = _51d_lru_seed ^ (long)args;
473
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200474 lru = lru64_get(XXH64(smp->data.u.str.area, smp->data.u.str.data, seed),
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000475 _51d_lru_tree, (void*)args, 0);
James Rosewella28bbd52015-09-18 18:28:52 +0100476 if (lru && lru->domain) {
James Rosewell63426cb2015-09-18 19:53:05 +0100477 _51d_retrieve_cache_entry(smp, lru);
James Rosewella28bbd52015-09-18 18:28:52 +0100478 return 1;
479 }
480 }
481
482 /* Create workset. This will later contain detection results. */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100483 ws = fiftyoneDegreesWorksetPoolGet(global_51degrees.pool);
James Rosewella28bbd52015-09-18 18:28:52 +0100484 if (!ws)
485 return 0;
486#endif
487
488 /* Duplicate the data and remove the "const" flag before device detection. */
489 if (!smp_dup(smp))
490 return 0;
491
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200492 smp->data.u.str.area[smp->data.u.str.data] = '\0';
James Rosewella28bbd52015-09-18 18:28:52 +0100493
494 /* Perform detection. */
495#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200496 fiftyoneDegreesMatch(ws, smp->data.u.str.area);
James Rosewella28bbd52015-09-18 18:28:52 +0100497 _51d_process_match(args, smp, ws);
498#endif
499#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200500 global_51degrees.device_offsets.firstOffset->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set,
501 smp->data.u.str.area);
Willy Tarreaub7a67142016-12-21 21:18:44 +0100502 global_51degrees.device_offsets.size = 1;
James Rosewella28bbd52015-09-18 18:28:52 +0100503 _51d_process_match(args, smp);
504#endif
505
506#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100507 fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws);
ben@51degrees.comd3842522016-01-08 13:52:32 +0000508 if (lru)
ben@51degrees.com82a9d762016-01-08 13:42:41 +0000509 _51d_insert_cache_entry(smp, lru, (void*)args);
James Rosewella28bbd52015-09-18 18:28:52 +0100510#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +0200511
Dragan Dosen93b38d92015-06-29 16:43:25 +0200512 return 1;
513}
514
James Rosewella28bbd52015-09-18 18:28:52 +0100515#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
516void _51d_init_http_headers()
517{
518 int index = 0;
519 const fiftyoneDegreesAsciiString *headerName;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100520 fiftyoneDegreesDataSet *ds = &global_51degrees.data_set;
521 global_51degrees.header_count = ds->httpHeadersCount;
Willy Tarreau83061a82018-07-13 11:56:34 +0200522 global_51degrees.header_names = malloc(global_51degrees.header_count * sizeof(struct buffer));
Willy Tarreaub7a67142016-12-21 21:18:44 +0100523 for (index = 0; index < global_51degrees.header_count; index++) {
James Rosewella28bbd52015-09-18 18:28:52 +0100524 headerName = fiftyoneDegreesGetString(ds, ds->httpHeaders[index].headerNameOffset);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200525 (global_51degrees.header_names + index)->area = (char*)&headerName->firstByte;
526 (global_51degrees.header_names + index)->data = headerName->length - 1;
527 (global_51degrees.header_names + index)->size = (global_51degrees.header_names + index)->data;
James Rosewella28bbd52015-09-18 18:28:52 +0100528 }
529}
530#endif
531
532#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
533void _51d_init_http_headers()
534{
535 int index = 0;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100536 fiftyoneDegreesDataSet *ds = &global_51degrees.data_set;
537 global_51degrees.header_count = fiftyoneDegreesGetHttpHeaderCount(ds);
538 global_51degrees.device_offsets.firstOffset = malloc(
539 global_51degrees.header_count * sizeof(fiftyoneDegreesDeviceOffset));
Willy Tarreau83061a82018-07-13 11:56:34 +0200540 global_51degrees.header_names = malloc(global_51degrees.header_count * sizeof(struct buffer));
Willy Tarreaub7a67142016-12-21 21:18:44 +0100541 global_51degrees.header_offsets = malloc(global_51degrees.header_count * sizeof(int32_t));
542 for (index = 0; index < global_51degrees.header_count; index++) {
543 global_51degrees.header_offsets[index] = fiftyoneDegreesGetHttpHeaderNameOffset(ds, index);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200544 global_51degrees.header_names->area = (char*)fiftyoneDegreesGetHttpHeaderNamePointer(ds, index);
545 global_51degrees.header_names->data = strlen(global_51degrees.header_names->area);
546 global_51degrees.header_names[index].size = global_51degrees.header_names->data;
James Rosewella28bbd52015-09-18 18:28:52 +0100547 }
548}
549#endif
550
Willy Tarreau9f3f2542016-12-21 20:30:05 +0100551/*
552 * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
553 */
554static int init_51degrees(void)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200555{
556 int i = 0;
Willy Tarreau83061a82018-07-13 11:56:34 +0200557 struct buffer *temp;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200558 struct _51d_property_names *name;
559 char **_51d_property_list = NULL;
560 fiftyoneDegreesDataSetInitStatus _51d_dataset_status = DATA_SET_INIT_STATUS_NOT_SET;
561
Willy Tarreaub7a67142016-12-21 21:18:44 +0100562 if (!global_51degrees.data_file_path)
Willy Tarreau9f3f2542016-12-21 20:30:05 +0100563 return 0;
Dragan Dosen9373fc52015-08-07 16:41:23 +0200564
Christopher Faulete8ca4342017-10-25 17:23:02 +0200565 if (global.nbthread > 1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100566 ha_alert("51Degrees: multithreading is not supported for now.\n");
Christopher Faulete8ca4342017-10-25 17:23:02 +0200567 return (ERR_FATAL | ERR_ALERT);
568 }
569
Willy Tarreaub7a67142016-12-21 21:18:44 +0100570 if (!LIST_ISEMPTY(&global_51degrees.property_names)) {
Dragan Dosen93b38d92015-06-29 16:43:25 +0200571 i = 0;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100572 list_for_each_entry(name, &global_51degrees.property_names, list)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200573 ++i;
574 _51d_property_list = calloc(i, sizeof(char *));
575
576 i = 0;
Willy Tarreaub7a67142016-12-21 21:18:44 +0100577 list_for_each_entry(name, &global_51degrees.property_names, list)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200578 _51d_property_list[i++] = name->name;
579 }
580
Willy Tarreaub7a67142016-12-21 21:18:44 +0100581 _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 +0200582
583 temp = get_trash_chunk();
584 chunk_reset(temp);
585
586 switch (_51d_dataset_status) {
587 case DATA_SET_INIT_STATUS_SUCCESS:
James Rosewella28bbd52015-09-18 18:28:52 +0100588#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
589 /* only 1 workset in the pool because HAProxy is currently single threaded
590 * this value should be set to the number of threads in future versions.
591 */
Willy Tarreaub7a67142016-12-21 21:18:44 +0100592 global_51degrees.pool = fiftyoneDegreesWorksetPoolCreate(&global_51degrees.data_set, NULL, 1);
James Rosewella28bbd52015-09-18 18:28:52 +0100593#endif
594 _51d_init_http_headers();
Dragan Dosen93b38d92015-06-29 16:43:25 +0200595 break;
596 case DATA_SET_INIT_STATUS_INSUFFICIENT_MEMORY:
597 chunk_printf(temp, "Insufficient memory.");
598 break;
599 case DATA_SET_INIT_STATUS_CORRUPT_DATA:
600#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
601 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Pattern data format.");
602#endif
603#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
604 chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Trie data format.");
605#endif
606 break;
607 case DATA_SET_INIT_STATUS_INCORRECT_VERSION:
608#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
609 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Pattern data format.");
610#endif
611#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
612 chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Trie data format.");
613#endif
614 break;
615 case DATA_SET_INIT_STATUS_FILE_NOT_FOUND:
616 chunk_printf(temp, "File not found.");
617 break;
ben51degrees1f077eb2016-07-06 12:07:21 +0100618 case DATA_SET_INIT_STATUS_NULL_POINTER:
619 chunk_printf(temp, "Null pointer to the existing dataset or memory location.");
620 break;
621 case DATA_SET_INIT_STATUS_POINTER_OUT_OF_BOUNDS:
622 chunk_printf(temp, "Allocated continuous memory containing 51Degrees data file appears to be smaller than expected. Most likely"
623 " because the data file was not fully loaded into the allocated memory.");
624 break;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200625 case DATA_SET_INIT_STATUS_NOT_SET:
626 chunk_printf(temp, "Data set not initialised.");
627 break;
Dragan Dosen483b93c2017-09-27 12:46:44 +0200628 default:
629 chunk_printf(temp, "Other error.");
630 break;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200631 }
632 if (_51d_dataset_status != DATA_SET_INIT_STATUS_SUCCESS) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200633 if (temp->data)
634 ha_alert("51Degrees Setup - Error reading 51Degrees data file. %s\n",
635 temp->area);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200636 else
Christopher Faulet767a84b2017-11-24 16:50:31 +0100637 ha_alert("51Degrees Setup - Error reading 51Degrees data file.\n");
Willy Tarreau9f3f2542016-12-21 20:30:05 +0100638 return ERR_ALERT | ERR_FATAL;
Dragan Dosen93b38d92015-06-29 16:43:25 +0200639 }
640 free(_51d_property_list);
641
James Rosewella28bbd52015-09-18 18:28:52 +0100642#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Dragan Dosen105c8e62015-06-29 16:43:26 +0200643 _51d_lru_seed = random();
Willy Tarreaub7a67142016-12-21 21:18:44 +0100644 if (global_51degrees.cache_size) {
645 _51d_lru_tree = lru64_new(global_51degrees.cache_size);
James Rosewella28bbd52015-09-18 18:28:52 +0100646 }
647#endif
Dragan Dosen105c8e62015-06-29 16:43:26 +0200648
Dragan Dosen93b38d92015-06-29 16:43:25 +0200649 return 0;
650}
651
Willy Tarreau7ac4c202016-12-21 20:59:01 +0100652static void deinit_51degrees(void)
Dragan Dosen93b38d92015-06-29 16:43:25 +0200653{
654 struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
655
Willy Tarreaub7a67142016-12-21 21:18:44 +0100656 free(global_51degrees.header_names);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200657#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100658 fiftyoneDegreesWorksetPoolFree(global_51degrees.pool);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200659#endif
660#ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED
Willy Tarreaub7a67142016-12-21 21:18:44 +0100661 free(global_51degrees.device_offsets.firstOffset);
662 free(global_51degrees.header_offsets);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200663#endif
Willy Tarreaub7a67142016-12-21 21:18:44 +0100664 fiftyoneDegreesDataSetFree(&global_51degrees.data_set);
Dragan Dosen93b38d92015-06-29 16:43:25 +0200665
Willy Tarreaub7a67142016-12-21 21:18:44 +0100666 free(global_51degrees.data_file_path); global_51degrees.data_file_path = NULL;
667 list_for_each_entry_safe(_51d_prop_name, _51d_prop_nameb, &global_51degrees.property_names, list) {
Dragan Dosen93b38d92015-06-29 16:43:25 +0200668 LIST_DEL(&_51d_prop_name->list);
669 free(_51d_prop_name);
670 }
Dragan Dosen105c8e62015-06-29 16:43:26 +0200671
James Rosewella28bbd52015-09-18 18:28:52 +0100672#ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED
Dragan Dosen105c8e62015-06-29 16:43:26 +0200673 while (lru64_destroy(_51d_lru_tree));
James Rosewella28bbd52015-09-18 18:28:52 +0100674#endif
Dragan Dosen93b38d92015-06-29 16:43:25 +0200675}
676
677static struct cfg_kw_list _51dcfg_kws = {{ }, {
678 { CFG_GLOBAL, "51degrees-data-file", _51d_data_file },
679 { CFG_GLOBAL, "51degrees-property-name-list", _51d_property_name_list },
680 { CFG_GLOBAL, "51degrees-property-separator", _51d_property_separator },
Dragan Dosen105c8e62015-06-29 16:43:26 +0200681 { CFG_GLOBAL, "51degrees-cache-size", _51d_cache_size },
Dragan Dosen93b38d92015-06-29 16:43:25 +0200682 { 0, NULL, NULL },
683}};
684
Willy Tarreau0108d902018-11-25 19:14:37 +0100685INITCALL1(STG_REGISTER, cfg_register_keywords, &_51dcfg_kws);
686
Dragan Dosen93b38d92015-06-29 16:43:25 +0200687/* Note: must not be declared <const> as its list will be overwritten */
James Rosewella28bbd52015-09-18 18:28:52 +0100688static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
689 { "51d.all", _51d_fetch, ARG5(1,STR,STR,STR,STR,STR), _51d_fetch_check, SMP_T_STR, SMP_USE_HRQHV },
690 { NULL, NULL, 0, 0, 0 },
691}};
692
Willy Tarreau0108d902018-11-25 19:14:37 +0100693INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
694
James Rosewella28bbd52015-09-18 18:28:52 +0100695/* Note: must not be declared <const> as its list will be overwritten */
Dragan Dosen93b38d92015-06-29 16:43:25 +0200696static struct sample_conv_kw_list conv_kws = {ILH, {
James Rosewella28bbd52015-09-18 18:28:52 +0100697 { "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 +0200698 { NULL, NULL, 0, 0, 0 },
699}};
700
Willy Tarreau0108d902018-11-25 19:14:37 +0100701INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
702
Willy Tarreau172f5ce2018-11-26 11:21:50 +0100703REGISTER_POST_CHECK(init_51degrees);
704REGISTER_POST_DEINIT(deinit_51degrees);
Willy Tarreau80713382018-11-26 10:19:54 +0100705REGISTER_BUILD_OPTS("Built with 51Degrees support.");