#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_51DEGREES_V4 #include #undef MAP_TYPE #include #else #include <51Degrees.h> #endif struct _51d_property_names { struct list list; char *name; }; #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED static struct lru64_head *_51d_lru_tree = NULL; static unsigned long long _51d_lru_seed; __decl_spinlock(_51d_lru_lock); #endif #ifdef FIFTYONE_DEGREES_HASH_INCLUDED #define _51D_HEADERS_BUFFER_SIZE BUFSIZE static THREAD_LOCAL struct { char **buf; int max; int count; } _51d_headers; static THREAD_LOCAL fiftyoneDegreesResultsHash *_51d_results = NULL; #endif static struct { char property_separator; /* the separator to use in the response for the values. this is taken from 51degrees-property-separator from config. */ struct list property_names; /* list of properties to load into the data set. this is taken from 51degrees-property-name-list from config. */ char *data_file_path; #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) int header_count; /* number of HTTP headers related to device detection. */ struct buffer *header_names; /* array of HTTP header names. */ fiftyoneDegreesDataSet data_set; /* data set used with the pattern and trie detection methods. */ #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED fiftyoneDegreesWorksetPool *pool; /* pool of worksets to avoid creating a new one for each request. */ #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED int32_t *header_offsets; /* offsets to the HTTP header name string. */ #ifdef FIFTYONEDEGREES_NO_THREADING fiftyoneDegreesDeviceOffsets device_offsets; /* Memory used for device offsets. */ #endif #endif #elif defined(FIFTYONE_DEGREES_HASH_INCLUDED) fiftyoneDegreesResourceManager manager; int use_perf_graph; int use_pred_graph; int drift; int difference; int allow_unmatched; #endif int cache_size; } global_51degrees = { .property_separator = ',', .property_names = LIST_HEAD_INIT(global_51degrees.property_names), .data_file_path = NULL, #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED .data_set = { }, #endif #ifdef FIFTYONE_DEGREES_HASH_INCLUDED .manager = { }, .use_perf_graph = -1, .use_pred_graph = -1, .drift = -1, .difference = -1, .allow_unmatched = -1, #endif .cache_size = 0, }; static int _51d_data_file(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "'%s' expects a filepath to a 51Degrees trie or pattern data file.", args[0]); return -1; } if (global_51degrees.data_file_path) free(global_51degrees.data_file_path); global_51degrees.data_file_path = strdup(args[1]); return 0; } static int _51d_property_name_list(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { int cur_arg = 1; struct _51d_property_names *name; if (*(args[cur_arg]) == 0) { memprintf(err, "'%s' expects at least one 51Degrees property name.", args[0]); return -1; } while (*(args[cur_arg])) { name = calloc(1, sizeof(*name)); name->name = strdup(args[cur_arg]); LIST_APPEND(&global_51degrees.property_names, &name->list); ++cur_arg; } return 0; } static int _51d_property_separator(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "'%s' expects a single character.", args[0]); return -1; } if (strlen(args[1]) > 1) { memprintf(err, "'%s' expects a single character, got '%s'.", args[0], args[1]); return -1; } global_51degrees.property_separator = *args[1]; return 0; } static int _51d_cache_size(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "'%s' expects a positive numeric value.", args[0]); return -1; } global_51degrees.cache_size = atoi(args[1]); if (global_51degrees.cache_size < 0) { memprintf(err, "'%s' expects a positive numeric value, got '%s'.", args[0], args[1]); return -1; } return 0; } static int _51d_fetch_check(struct arg *arg, char **err_msg) { if (global_51degrees.data_file_path) return 1; memprintf(err_msg, "51Degrees data file is not specified (parameter '51degrees-data-file')"); return 0; } static int _51d_conv_check(struct arg *arg, struct sample_conv *conv, const char *file, int line, char **err_msg) { if (global_51degrees.data_file_path) return 1; memprintf(err_msg, "51Degrees data file is not specified (parameter '51degrees-data-file')"); return 0; } #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED static void _51d_lru_free(void *cache_entry) { struct buffer *ptr = cache_entry; if (!ptr) return; free(ptr->area); free(ptr); } /* Allocates memory freeing space in the cache if necessary. */ static void *_51d_malloc(int size) { void *ptr = malloc(size); if (!ptr) { /* free the oldest 10 entries from lru to free up some memory * then try allocating memory again */ lru64_kill_oldest(_51d_lru_tree, 10); ptr = malloc(size); } return ptr; } /* Insert the data associated with the sample into the cache as a fresh item. */ static void _51d_insert_cache_entry(struct sample *smp, struct lru64 *lru, void* domain) { struct buffer *cache_entry = _51d_malloc(sizeof(*cache_entry)); if (!cache_entry) return; cache_entry->area = _51d_malloc(smp->data.u.str.data + 1); if (!cache_entry->area) { free(cache_entry); return; } memcpy(cache_entry->area, smp->data.u.str.area, smp->data.u.str.data); cache_entry->area[smp->data.u.str.data] = 0; cache_entry->data = smp->data.u.str.data; HA_SPIN_LOCK(OTHER_LOCK, &_51d_lru_lock); lru64_commit(lru, cache_entry, domain, 0, _51d_lru_free); HA_SPIN_UNLOCK(OTHER_LOCK, &_51d_lru_lock); } /* Retrieves the data from the cache and sets the sample data to this string. */ static void _51d_retrieve_cache_entry(struct sample *smp, struct lru64 *lru) { struct buffer *cache_entry = lru->data; smp->data.u.str.area = cache_entry->area; smp->data.u.str.data = cache_entry->data; } #endif #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED /* Sets the important HTTP headers ahead of the detection */ static void _51d_set_headers(struct sample *smp, fiftyoneDegreesWorkset *ws) { struct channel *chn; struct htx *htx; struct http_hdr_ctx ctx; struct ist name; int i; ws->importantHeadersCount = 0; chn = (smp->strm ? &smp->strm->req : NULL); // No need to null check as this has already been carried out in the // calling method htx = smp_prefetch_htx(smp, chn, NULL, 1); ALREADY_CHECKED(htx); for (i = 0; i < global_51degrees.header_count; i++) { name = ist2((global_51degrees.header_names + i)->area, (global_51degrees.header_names + i)->data); ctx.blk = NULL; if (http_find_header(htx, name, &ctx, 1)) { ws->importantHeaders[ws->importantHeadersCount].header = ws->dataSet->httpHeaders + i; ws->importantHeaders[ws->importantHeadersCount].headerValue = ctx.value.ptr; ws->importantHeaders[ws->importantHeadersCount].headerValueLength = ctx.value.len; ws->importantHeadersCount++; } } } #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) { int i; for (i = 0; i < global_51degrees.data_set.uniqueHttpHeaders.count; i++) { offsets->firstOffset[i].userAgent = NULL; } } static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets) { struct channel *chn; struct htx *htx; struct http_hdr_ctx ctx; struct ist name; int i; offsets->size = 0; chn = (smp->strm ? &smp->strm->req : NULL); // No need to null check as this has already been carried out in the // calling method htx = smp_prefetch_htx(smp, chn, NULL, 1); ALREADY_CHECKED(htx); for (i = 0; i < global_51degrees.header_count; i++) { name = ist2((global_51degrees.header_names + i)->area, (global_51degrees.header_names + i)->data); ctx.blk = NULL; if (http_find_header(htx, name, &ctx, 1)) { (offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i); (offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr); offsets->size++; } } } #endif #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED /* Provides a hash code for the important HTTP headers. */ unsigned long long _51d_req_hash(const struct arg *args, fiftyoneDegreesWorkset* ws) { unsigned long long seed = _51d_lru_seed ^ (long)args; unsigned long long hash = 0; int i; for(i = 0; i < ws->importantHeadersCount; i++) { hash ^= ws->importantHeaders[i].header->headerNameOffset; hash ^= XXH3(ws->importantHeaders[i].headerValue, ws->importantHeaders[i].headerValueLength, seed); } return hash; } #endif #ifdef FIFTYONE_DEGREES_HASH_INCLUDED static int _51d_use_perf_graph(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (too_many_args(1, args, err, NULL)) return -1; if (strcmp(args[1], "on") == 0) global_51degrees.use_perf_graph = 1; else if (strcmp(args[1], "off") == 0) global_51degrees.use_perf_graph = 0; else { memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]); return -1; } return 0; } static int _51d_use_pred_graph(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (too_many_args(1, args, err, NULL)) return -1; if (strcmp(args[1], "on") == 0) global_51degrees.use_pred_graph = 1; else if (strcmp(args[1], "off") == 0) global_51degrees.use_pred_graph = 0; else { memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]); return -1; } return 0; } static int _51d_drift(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "'%s' expects a positive numeric value.", args[0]); return -1; } global_51degrees.drift = atoi(args[1]); if (global_51degrees.drift < 0) { memprintf(err, "'%s' expects a positive numeric value, got '%s'.", args[0], args[1]); return -1; } return 0; } static int _51d_difference(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "'%s' expects a positive numeric value.", args[0]); return -1; } global_51degrees.difference = atoi(args[1]); if (global_51degrees.difference < 0) { memprintf(err, "'%s' expects a positive numeric value, got '%s'.", args[0], args[1]); return -1; } return 0; } static int _51d_allow_unmatched(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) { if (too_many_args(1, args, err, NULL)) return -1; if (strcmp(args[1], "on") == 0) global_51degrees.allow_unmatched = 1; else if (strcmp(args[1], "off") == 0) global_51degrees.allow_unmatched = 0; else { memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]); return -1; } return 0; } static int _51d_init_internal() { fiftyoneDegreesDataSetHash *ds; int hdr_count; int i, ret = 0; ds = (fiftyoneDegreesDataSetHash *)fiftyoneDegreesDataSetGet(&global_51degrees.manager); hdr_count = ds->b.b.uniqueHeaders->count; if (hdr_count > _51d_headers.max) hdr_count = _51d_headers.max; _51d_results = fiftyoneDegreesResultsHashCreate(&global_51degrees.manager, hdr_count, 0); if (!_51d_results) goto out; for (i = 0; i < hdr_count; i++) { _51d_headers.buf[i] = malloc(_51D_HEADERS_BUFFER_SIZE); if (!_51d_headers.buf[i]) goto out; _51d_headers.count++; } /* success */ ret = 1; out: fiftyoneDegreesDataSetRelease((fiftyoneDegreesDataSetBase *)ds); return ret; } static fiftyoneDegreesEvidenceKeyValuePairArray * _51d_get_evidence(struct sample *smp) { fiftyoneDegreesEvidenceKeyValuePairArray *evidence; fiftyoneDegreesDataSetHash *ds; size_t size; struct channel *chn; struct htx *htx; struct http_hdr_ctx ctx; struct ist name; int i; chn = (smp->strm ? &smp->strm->req : NULL); // No need to null check as this has already been carried out in the // calling method htx = smp_prefetch_htx(smp, chn, NULL, 1); ALREADY_CHECKED(htx); ds = (fiftyoneDegreesDataSetHash *)_51d_results->b.b.dataSet; size = _51d_headers.count * 2; evidence = fiftyoneDegreesEvidenceCreate(size); if (!evidence) return NULL; for (i = 0; i < _51d_headers.count; i++) { fiftyoneDegreesHeader *hdr = &ds->b.b.uniqueHeaders->items[i]; name = ist2(hdr->name, hdr->nameLength); ctx.blk = NULL; if (http_find_header(htx, name, &ctx, 1)) { size_t len = ctx.value.len; if (unlikely(len >= _51D_HEADERS_BUFFER_SIZE)) len = _51D_HEADERS_BUFFER_SIZE - 1; memcpy(_51d_headers.buf[i], ctx.value.ptr, len); _51d_headers.buf[i][len] = '\0'; fiftyoneDegreesEvidenceAddString( evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, name.ptr, _51d_headers.buf[i]); } } return evidence; } #endif #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED static void _51d_process_match(const struct arg *args, struct sample *smp, fiftyoneDegreesWorkset* ws) { char *methodName; #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED static void _51d_process_match(const struct arg *args, struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets) { char valuesBuffer[1024]; const char **requiredProperties = fiftyoneDegreesGetRequiredPropertiesNames(&global_51degrees.data_set); int requiredPropertiesCount = fiftyoneDegreesGetRequiredPropertiesCount(&global_51degrees.data_set); #endif const char* property_name; int j; #elif defined(FIFTYONE_DEGREES_HASH_INCLUDED) static void _51d_process_match(const struct arg *args, struct sample *smp) { char valuesBuffer[1024]; #endif char no_data[] = "NoData"; /* response when no data could be found */ struct buffer *temp = get_trash_chunk(); int i = 0, found; #if defined(FIFTYONE_DEGREES_HASH_INCLUDED) FIFTYONE_DEGREES_EXCEPTION_CREATE; #endif /* Loop through property names passed to the filter and fetch them from the dataset. */ while (args[i].data.str.area) { /* Try to find request property in dataset. */ found = 0; #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED if (strcmp("Method", args[i].data.str.area) == 0) { switch(ws->method) { case EXACT: methodName = "Exact"; break; case NUMERIC: methodName = "Numeric"; break; case NEAREST: methodName = "Nearest"; break; case CLOSEST: methodName = "Closest"; break; default: case NONE: methodName = "None"; break; } chunk_appendf(temp, "%s", methodName); found = 1; } else if (strcmp("Difference", args[i].data.str.area) == 0) { chunk_appendf(temp, "%d", ws->difference); found = 1; } else if (strcmp("Rank", args[i].data.str.area) == 0) { chunk_appendf(temp, "%d", fiftyoneDegreesGetSignatureRank(ws)); found = 1; } else { for (j = 0; j < ws->dataSet->requiredPropertyCount; j++) { property_name = fiftyoneDegreesGetPropertyName(ws->dataSet, ws->dataSet->requiredProperties[j]); if (strcmp(property_name, args[i].data.str.area) == 0) { found = 1; fiftyoneDegreesSetValues(ws, j); chunk_appendf(temp, "%s", fiftyoneDegreesGetValueName(ws->dataSet, *ws->values)); break; } } } #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED found = 0; for (j = 0; j < requiredPropertiesCount; j++) { property_name = requiredProperties[j]; if (strcmp(property_name, args[i].data.str.area) == 0 && fiftyoneDegreesGetValueFromOffsets(&global_51degrees.data_set, offsets, j, valuesBuffer, 1024) > 0) { found = 1; chunk_appendf(temp, "%s", valuesBuffer); break; } } #endif #ifdef FIFTYONE_DEGREES_HASH_INCLUDED FIFTYONE_DEGREES_EXCEPTION_CLEAR; found = fiftyoneDegreesResultsHashGetValuesString( _51d_results, args[i].data.str.area, valuesBuffer, 1024, "|", exception); if (FIFTYONE_DEGREES_EXCEPTION_FAILED || found <= 0) found = 0; else chunk_appendf(temp, "%s", valuesBuffer); #endif if (!found) chunk_appendf(temp, "%s", no_data); /* Add separator. */ chunk_appendf(temp, "%c", global_51degrees.property_separator); ++i; } if (temp->data) { --temp->data; temp->area[temp->data] = '\0'; } smp->data.u.str.area = temp->area; smp->data.u.str.data = temp->data; } /* Sets the sample data as a constant string. This ensures that the * string will be processed correctly. */ static void _51d_set_smp(struct sample *smp) { /* * Data type has to be set to ensure the string output is processed * correctly. */ smp->data.type = SMP_T_STR; /* Flags the sample to show it uses constant memory. */ smp->flags |= SMP_F_CONST; } static int _51d_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct channel *chn; struct htx *htx; #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED fiftyoneDegreesWorkset* ws; /* workset for detection */ struct lru64 *lru = NULL; #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED fiftyoneDegreesDeviceOffsets *offsets; /* Offsets for detection */ #endif #ifdef FIFTYONE_DEGREES_HASH_INCLUDED fiftyoneDegreesEvidenceKeyValuePairArray *evidence = NULL; FIFTYONE_DEGREES_EXCEPTION_CREATE; #endif chn = (smp->strm ? &smp->strm->req : NULL); htx = smp_prefetch_htx(smp, chn, NULL, 1); if (!htx) return 0; #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED /* Get only the headers needed for device detection so they can be used * with the cache to return previous results. Pattern is slower than * Trie so caching will help improve performance. */ /* Get a workset from the pool which will later contain detection results. */ ws = fiftyoneDegreesWorksetPoolGet(global_51degrees.pool); if (!ws) return 0; /* Set the important HTTP headers for this request in the workset. */ _51d_set_headers(smp, ws); /* Check the cache to see if there's results for these headers already. */ if (_51d_lru_tree) { HA_SPIN_LOCK(OTHER_LOCK, &_51d_lru_lock); lru = lru64_get(_51d_req_hash(args, ws), _51d_lru_tree, (void*)args, 0); if (lru && lru->domain) { fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws); _51d_retrieve_cache_entry(smp, lru); HA_SPIN_UNLOCK(OTHER_LOCK, &_51d_lru_lock); _51d_set_smp(smp); return 1; } HA_SPIN_UNLOCK(OTHER_LOCK, &_51d_lru_lock); } fiftyoneDegreesMatchForHttpHeaders(ws); _51d_process_match(args, smp, ws); #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED #ifndef FIFTYONEDEGREES_NO_THREADING offsets = fiftyoneDegreesCreateDeviceOffsets(&global_51degrees.data_set); _51d_init_device_offsets(offsets); #else offsets = &global_51degrees.device_offsets; #endif /* Trie is very fast so all the headers can be passed in and the result * returned faster than the hashing algorithm process. */ _51d_set_device_offsets(smp, offsets); _51d_process_match(args, smp, offsets); #ifndef FIFTYONEDEGREES_NO_THREADING fiftyoneDegreesFreeDeviceOffsets(offsets); #endif #endif #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws); if (lru) _51d_insert_cache_entry(smp, lru, (void*)args); #endif #ifdef FIFTYONE_DEGREES_HASH_INCLUDED evidence = _51d_get_evidence(smp); if (!evidence) return 0; fiftyoneDegreesResultsHashFromEvidence( _51d_results, evidence, exception); fiftyoneDegreesEvidenceFree(evidence); if (FIFTYONE_DEGREES_EXCEPTION_FAILED) return 0; _51d_process_match(args, smp); #endif _51d_set_smp(smp); return 1; } static int _51d_conv(const struct arg *args, struct sample *smp, void *private) { #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED fiftyoneDegreesWorkset* ws; /* workset for detection */ struct lru64 *lru = NULL; #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED fiftyoneDegreesDeviceOffsets *offsets; /* Offsets for detection */ #endif #ifdef FIFTYONE_DEGREES_HASH_INCLUDED FIFTYONE_DEGREES_EXCEPTION_CREATE; #endif #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED /* Look in the list. */ if (_51d_lru_tree) { unsigned long long seed = _51d_lru_seed ^ (long)args; HA_SPIN_LOCK(OTHER_LOCK, &_51d_lru_lock); lru = lru64_get(XXH3(smp->data.u.str.area, smp->data.u.str.data, seed), _51d_lru_tree, (void*)args, 0); if (lru && lru->domain) { _51d_retrieve_cache_entry(smp, lru); HA_SPIN_UNLOCK(OTHER_LOCK, &_51d_lru_lock); return 1; } HA_SPIN_UNLOCK(OTHER_LOCK, &_51d_lru_lock); } /* Create workset. This will later contain detection results. */ ws = fiftyoneDegreesWorksetPoolGet(global_51degrees.pool); if (!ws) return 0; #endif /* Duplicate the data and remove the "const" flag before device detection. */ if (!smp_dup(smp)) return 0; smp->data.u.str.area[smp->data.u.str.data] = '\0'; /* Perform detection. */ #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED fiftyoneDegreesMatch(ws, smp->data.u.str.area); _51d_process_match(args, smp, ws); #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED #ifndef FIFTYONEDEGREES_NO_THREADING offsets = fiftyoneDegreesCreateDeviceOffsets(&global_51degrees.data_set); _51d_init_device_offsets(offsets); #else offsets = &global_51degrees.device_offsets; #endif offsets->firstOffset->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, smp->data.u.str.area); offsets->size = 1; _51d_process_match(args, smp, offsets); #endif #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED fiftyoneDegreesWorksetPoolRelease(global_51degrees.pool, ws); if (lru) _51d_insert_cache_entry(smp, lru, (void*)args); #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED #ifndef FIFTYONEDEGREES_NO_THREADING fiftyoneDegreesFreeDeviceOffsets(offsets); #endif #endif #elif defined(FIFTYONE_DEGREES_HASH_INCLUDED) fiftyoneDegreesResultsHashFromUserAgent(_51d_results, smp->data.u.str.area, smp->data.u.str.data, exception); if (FIFTYONE_DEGREES_EXCEPTION_FAILED) return 0; _51d_process_match(args, smp); #endif _51d_set_smp(smp); return 1; } #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED void _51d_init_http_headers() { int index = 0; const fiftyoneDegreesAsciiString *headerName; fiftyoneDegreesDataSet *ds = &global_51degrees.data_set; global_51degrees.header_count = ds->httpHeadersCount; global_51degrees.header_names = malloc(global_51degrees.header_count * sizeof(struct buffer)); for (index = 0; index < global_51degrees.header_count; index++) { headerName = fiftyoneDegreesGetString(ds, ds->httpHeaders[index].headerNameOffset); (global_51degrees.header_names + index)->area = (char*)&headerName->firstByte; (global_51degrees.header_names + index)->data = headerName->length - 1; (global_51degrees.header_names + index)->size = (global_51degrees.header_names + index)->data; } } #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED void _51d_init_http_headers() { int index = 0; fiftyoneDegreesDataSet *ds = &global_51degrees.data_set; global_51degrees.header_count = fiftyoneDegreesGetHttpHeaderCount(ds); #ifdef FIFTYONEDEGREES_NO_THREADING global_51degrees.device_offsets.firstOffset = malloc( global_51degrees.header_count * sizeof(fiftyoneDegreesDeviceOffset)); _51d_init_device_offsets(&global_51degrees.device_offsets); #endif global_51degrees.header_names = malloc(global_51degrees.header_count * sizeof(struct buffer)); global_51degrees.header_offsets = malloc(global_51degrees.header_count * sizeof(int32_t)); for (index = 0; index < global_51degrees.header_count; index++) { global_51degrees.header_offsets[index] = fiftyoneDegreesGetHttpHeaderNameOffset(ds, index); global_51degrees.header_names[index].area = (char*)fiftyoneDegreesGetHttpHeaderNamePointer(ds, index); global_51degrees.header_names[index].data = strlen(global_51degrees.header_names[index].area); global_51degrees.header_names[index].size = global_51degrees.header_names->data; } } #endif /* * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*. */ static int init_51degrees(void) { int i = 0; struct _51d_property_names *name; char **_51d_property_list = NULL; #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) struct buffer *temp; fiftyoneDegreesDataSetInitStatus _51d_dataset_status = DATA_SET_INIT_STATUS_NOT_SET; #elif defined(FIFTYONE_DEGREES_HASH_INCLUDED) fiftyoneDegreesConfigHash config = fiftyoneDegreesHashInMemoryConfig; fiftyoneDegreesPropertiesRequired properties = fiftyoneDegreesPropertiesDefault; fiftyoneDegreesMemoryReader reader; fiftyoneDegreesStatusCode status; FIFTYONE_DEGREES_EXCEPTION_CREATE; #endif if (!global_51degrees.data_file_path) return ERR_NONE; if (global.nbthread < 1) { ha_alert("51Degrees: The thread count cannot be zero or negative.\n"); return (ERR_FATAL | ERR_ALERT); } if (!LIST_ISEMPTY(&global_51degrees.property_names)) { i = 0; list_for_each_entry(name, &global_51degrees.property_names, list) ++i; _51d_property_list = calloc(i, sizeof(*_51d_property_list)); i = 0; list_for_each_entry(name, &global_51degrees.property_names, list) _51d_property_list[i++] = name->name; } #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) _51d_dataset_status = fiftyoneDegreesInitWithPropertyArray(global_51degrees.data_file_path, &global_51degrees.data_set, (const char**)_51d_property_list, i); temp = get_trash_chunk(); chunk_reset(temp); switch (_51d_dataset_status) { case DATA_SET_INIT_STATUS_SUCCESS: #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED global_51degrees.pool = fiftyoneDegreesWorksetPoolCreate(&global_51degrees.data_set, NULL, global.nbthread); #endif _51d_init_http_headers(); break; case DATA_SET_INIT_STATUS_INSUFFICIENT_MEMORY: chunk_printf(temp, "Insufficient memory."); break; case DATA_SET_INIT_STATUS_CORRUPT_DATA: #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Pattern data format."); #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED chunk_printf(temp, "Corrupt data file. Check that the data file provided is uncompressed and Trie data format."); #endif break; case DATA_SET_INIT_STATUS_INCORRECT_VERSION: #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Pattern data format."); #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED chunk_printf(temp, "Incorrect version. Check that the data file provided is uncompressed and Trie data format."); #endif break; case DATA_SET_INIT_STATUS_FILE_NOT_FOUND: chunk_printf(temp, "File not found."); break; case DATA_SET_INIT_STATUS_NULL_POINTER: chunk_printf(temp, "Null pointer to the existing dataset or memory location."); break; case DATA_SET_INIT_STATUS_POINTER_OUT_OF_BOUNDS: chunk_printf(temp, "Allocated continuous memory containing 51Degrees data file appears to be smaller than expected. Most likely" " because the data file was not fully loaded into the allocated memory."); break; case DATA_SET_INIT_STATUS_NOT_SET: chunk_printf(temp, "Data set not initialised."); break; default: chunk_printf(temp, "Other error."); break; } if (_51d_dataset_status != DATA_SET_INIT_STATUS_SUCCESS) { if (temp->data) ha_alert("51Degrees Setup - Error reading 51Degrees data file. %s\n", temp->area); else ha_alert("51Degrees Setup - Error reading 51Degrees data file.\n"); return ERR_ALERT | ERR_FATAL; } free(_51d_property_list); #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED _51d_lru_seed = ha_random(); if (global_51degrees.cache_size) { _51d_lru_tree = lru64_new(global_51degrees.cache_size); } #endif #elif defined(FIFTYONE_DEGREES_HASH_INCLUDED) config.b.b.freeData = true; if (global_51degrees.use_perf_graph != -1) config.usePerformanceGraph = global_51degrees.use_perf_graph; if (global_51degrees.use_pred_graph != -1) config.usePredictiveGraph = global_51degrees.use_pred_graph; if (global_51degrees.drift > 0) config.drift = global_51degrees.drift; if (global_51degrees.difference > 0) config.difference = global_51degrees.difference; if (global_51degrees.allow_unmatched != -1) config.b.allowUnmatched = global_51degrees.allow_unmatched; config.strings.concurrency = config.properties.concurrency = config.values.concurrency = config.profiles.concurrency = config.nodes.concurrency = config.profileOffsets.concurrency = config.maps.concurrency = config.components.concurrency = config.rootNodes.concurrency = global.nbthread; properties.array = (const char **)_51d_property_list; properties.count = i; status = fiftyoneDegreesFileReadToByteArray(global_51degrees.data_file_path, &reader); if (status == FIFTYONE_DEGREES_STATUS_SUCCESS && !FIFTYONE_DEGREES_EXCEPTION_FAILED) { FIFTYONE_DEGREES_EXCEPTION_CLEAR; status = fiftyoneDegreesHashInitManagerFromMemory( &global_51degrees.manager, &config, &properties, reader.startByte, reader.length, exception); } free(_51d_property_list); _51d_property_list = NULL; i = 0; if (status != FIFTYONE_DEGREES_STATUS_SUCCESS || FIFTYONE_DEGREES_EXCEPTION_FAILED) { const char *message = fiftyoneDegreesStatusGetMessage(status, global_51degrees.data_file_path); if (message) ha_alert("51Degrees Setup - Error reading 51Degrees data file. %s\n", message); else ha_alert("51Degrees Setup - Error reading 51Degrees data file.\n"); return ERR_ALERT | ERR_FATAL; } #endif return ERR_NONE; } static void deinit_51degrees(void) { struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb; #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) free(global_51degrees.header_names); #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED if (global_51degrees.pool) fiftyoneDegreesWorksetPoolFree(global_51degrees.pool); #endif #ifdef FIFTYONEDEGREES_H_TRIE_INCLUDED #ifdef FIFTYONEDEGREES_NO_THREADING free(global_51degrees.device_offsets.firstOffset); #endif free(global_51degrees.header_offsets); #endif fiftyoneDegreesDataSetFree(&global_51degrees.data_set); #endif ha_free(&global_51degrees.data_file_path); list_for_each_entry_safe(_51d_prop_name, _51d_prop_nameb, &global_51degrees.property_names, list) { LIST_DELETE(&_51d_prop_name->list); free(_51d_prop_name); } #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED while (lru64_destroy(_51d_lru_tree)); #endif } #ifdef FIFTYONE_DEGREES_HASH_INCLUDED static int init_51degrees_per_thread() { if (!global_51degrees.data_file_path) { /* noop */ return 1; } _51d_headers.max = global.tune.max_http_hdr; _51d_headers.buf = calloc(_51d_headers.max, sizeof(*_51d_headers.buf)); _51d_headers.count = 0; if (!_51d_headers.buf) return 0; if (!_51d_init_internal()) return 0; return 1; } static void deinit_51degrees_per_thread() { int i; if (_51d_results) { fiftyoneDegreesResultsHashFree(_51d_results); _51d_results = NULL; } if (_51d_headers.buf) { for (i = 0; i < _51d_headers.max; i++) free(_51d_headers.buf[i]); free(_51d_headers.buf); _51d_headers.buf = NULL; } _51d_headers.max = 0; _51d_headers.count = 0; } #endif static struct cfg_kw_list _51dcfg_kws = {{ }, { { CFG_GLOBAL, "51degrees-data-file", _51d_data_file }, { CFG_GLOBAL, "51degrees-property-name-list", _51d_property_name_list }, { CFG_GLOBAL, "51degrees-property-separator", _51d_property_separator }, { CFG_GLOBAL, "51degrees-cache-size", _51d_cache_size }, #ifdef FIFTYONE_DEGREES_HASH_INCLUDED { CFG_GLOBAL, "51degrees-use-performance-graph", _51d_use_perf_graph }, { CFG_GLOBAL, "51degrees-use-predictive-graph", _51d_use_pred_graph }, { CFG_GLOBAL, "51degrees-drift", _51d_drift }, { CFG_GLOBAL, "51degrees-difference", _51d_difference }, { CFG_GLOBAL, "51degrees-allow-unmatched", _51d_allow_unmatched }, #endif { 0, NULL, NULL }, }}; INITCALL1(STG_REGISTER, cfg_register_keywords, &_51dcfg_kws); /* Note: must not be declared as its list will be overwritten */ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "51d.all", _51d_fetch, ARG5(1,STR,STR,STR,STR,STR), _51d_fetch_check, SMP_T_STR, SMP_USE_HRQHV }, { NULL, NULL, 0, 0, 0 }, }}; INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords); /* Note: must not be declared as its list will be overwritten */ static struct sample_conv_kw_list conv_kws = {ILH, { { "51d.single", _51d_conv, ARG5(1,STR,STR,STR,STR,STR), _51d_conv_check, SMP_T_STR, SMP_T_STR }, { NULL, NULL, 0, 0, 0 }, }}; INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws); REGISTER_POST_CHECK(init_51degrees); REGISTER_POST_DEINIT(deinit_51degrees); #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) #ifndef FIFTYONEDEGREES_DUMMY_LIB REGISTER_BUILD_OPTS("Built with 51Degrees Pattern support."); #else REGISTER_BUILD_OPTS("Built with 51Degrees Pattern support (dummy library)."); #endif #elif defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) #ifndef FIFTYONEDEGREES_DUMMY_LIB REGISTER_BUILD_OPTS("Built with 51Degrees Trie support."); #else REGISTER_BUILD_OPTS("Built with 51Degrees Trie support (dummy library)."); #endif #elif defined(FIFTYONE_DEGREES_HASH_INCLUDED) REGISTER_PER_THREAD_INIT(init_51degrees_per_thread); REGISTER_PER_THREAD_DEINIT(deinit_51degrees_per_thread); #ifndef FIFTYONEDEGREES_DUMMY_LIB REGISTER_BUILD_OPTS("Built with 51Degrees V4 Hash support."); #else REGISTER_BUILD_OPTS("Built with 51Degrees V4 Hash support (dummy library)."); #endif #endif