diff --git a/src/da.c b/src/da.c index 7f507ead4..e1886b8e8 100644 --- a/src/da.c +++ b/src/da.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,20 @@ static int da_property_separator(char **args, int section_type, struct proxy *cu return 0; } +static int da_properties_cookie(char **args, int section_type, struct proxy *curpx, + struct proxy *defpx, const char *file, int line, + char **err) +{ + if (*(args[1]) == 0) { + memprintf(err, "deviceatlas cookie name : expects a string argument.\n"); + return -1; + } else { + global.deviceatlas.cookiename = strdup(args[1]); + } + global.deviceatlas.cookienamelen = strlen(global.deviceatlas.cookiename); + return 0; +} + static size_t da_haproxy_read(void *ctx, size_t len, char *buf) { return fread(buf, 1, len, ctx); @@ -70,6 +85,8 @@ static void da_haproxy_log(da_severity_t severity, da_status_t status, } } +#define DA_COOKIENAME_DEFAULT "DAPROPS" + int init_deviceatlas(void) { da_status_t status = DA_SYS; @@ -105,8 +122,14 @@ int init_deviceatlas(void) goto out; } + if (global.deviceatlas.cookiename == 0) { + global.deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT); + global.deviceatlas.cookienamelen = strlen(global.deviceatlas.cookiename); + } + global.deviceatlas.useragentid = da_atlas_header_evidence_id(&global.deviceatlas.atlas, "user-agent"); + global.deviceatlas.daset = 1; fprintf(stdout, "Deviceatlas module loaded.\n"); } @@ -121,7 +144,8 @@ void deinit_deviceatlas(void) free(global.deviceatlas.jsonpath); } - if (global.deviceatlas.useragentid > 0) { + if (global.deviceatlas.daset == 1) { + free(global.deviceatlas.cookiename); da_atlas_close(&global.deviceatlas.atlas); free(global.deviceatlas.atlasimgptr); } @@ -129,38 +153,21 @@ void deinit_deviceatlas(void) da_fini(); } -static int da_haproxy(const struct arg *args, struct sample *smp, void *private) +static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo) { struct chunk *tmp; - da_deviceinfo_t devinfo; da_propid_t prop, *pprop; - da_type_t proptype; da_status_t status; - const char *useragent, *propname; - char useragentbuf[1024]; + da_type_t proptype; + const char *propname; int i; - if (global.deviceatlas.useragentid == 0) { - return 1; - } - tmp = get_trash_chunk(); chunk_reset(tmp); - i = smp->data.u.str.len > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.len; - memcpy(useragentbuf, smp->data.u.str.str, i - 1); - useragentbuf[i - 1] = 0; - - useragent = (const char *)useragentbuf; propname = (const char *)args[0].data.str.str; i = 0; - status = da_search(&global.deviceatlas.atlas, &devinfo, - global.deviceatlas.useragentid, useragent, 0); - if (status != DA_OK) { - return 0; - } - for (; propname != 0; i ++, propname = (const char *)args[i].data.str.str) { status = da_atlas_getpropid(&global.deviceatlas.atlas, propname, &prop); @@ -174,7 +181,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, void *private) switch (proptype) { case DA_TYPE_BOOLEAN: { bool val; - status = da_getpropboolean(&devinfo, *pprop, &val); + status = da_getpropboolean(devinfo, *pprop, &val); if (status == DA_OK) { chunk_appendf(tmp, "%d", val); } @@ -183,7 +190,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, void *private) case DA_TYPE_INTEGER: case DA_TYPE_NUMBER: { long val; - status = da_getpropinteger(&devinfo, *pprop, &val); + status = da_getpropinteger(devinfo, *pprop, &val); if (status == DA_OK) { chunk_appendf(tmp, "%ld", val); } @@ -191,12 +198,12 @@ static int da_haproxy(const struct arg *args, struct sample *smp, void *private) } case DA_TYPE_STRING: { const char *val; - status = da_getpropstring(&devinfo, *pprop, &val); + status = da_getpropstring(devinfo, *pprop, &val); if (status == DA_OK) { chunk_appendf(tmp, "%s", val); } break; - } + } default: break; } @@ -204,7 +211,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, void *private) chunk_appendf(tmp, "%c", global.deviceatlas.separator); } - da_close(&devinfo); + da_close(devinfo); if (tmp->len) { --tmp->len; @@ -212,21 +219,141 @@ static int da_haproxy(const struct arg *args, struct sample *smp, void *private) } smp->data.u.str.str = tmp->str; - smp->data.u.str.len = strlen(tmp->str); + smp->data.u.str.len = tmp->len; return 1; } +static int da_haproxy_conv(const struct arg *args, struct sample *smp, void *private) +{ + da_deviceinfo_t devinfo; + da_status_t status; + const char *useragent; + char useragentbuf[1024] = { 0 }; + int i; + + if (global.deviceatlas.daset == 0) { + return 1; + } + + i = smp->data.u.str.len > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.len; + memcpy(useragentbuf, smp->data.u.str.str, i - 1); + useragentbuf[i - 1] = 0; + + useragent = (const char *)useragentbuf; + + status = da_search(&global.deviceatlas.atlas, &devinfo, + global.deviceatlas.useragentid, useragent, 0); + + return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo); +} + +#define DA_MAX_HEADERS 24 + +static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct hdr_idx *hidx; + struct hdr_ctx hctx; + const struct http_msg *hmsg; + da_evidence_t ev[DA_MAX_HEADERS]; + da_deviceinfo_t devinfo; + da_status_t status; + char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }}; + int i, nbh = 0; + + if (global.deviceatlas.daset == 0) { + return 1; + } + + CHECK_HTTP_MESSAGE_FIRST(); + smp->data.type = SMP_T_STR; + + /** + * Here we go through the whole list of headers from start + * they will be filtered via the DeviceAtlas API itself + */ + hctx.idx = 0; + hidx = &smp->strm->txn->hdr_idx; + hmsg = &smp->strm->txn->req; + + while (http_find_next_header(hmsg->chn->buf->p, hidx, &hctx) == 1 && + nbh < DA_MAX_HEADERS) { + char *pval; + size_t vlen; + da_evidence_id_t evid = -1; + char hbuf[24] = { 0 }; + + /* The HTTP headers used by the DeviceAtlas API are not longer */ + if (hctx.del >= sizeof(hbuf)) { + continue; + } + + vlen = hctx.vlen; + memcpy(hbuf, hctx.line, hctx.del); + hbuf[hctx.del] = 0; + pval = (hctx.line + hctx.val); + + if (strcmp(hbuf, "Accept-Language") == 0) { + evid = da_atlas_accept_language_evidence_id(&global.deviceatlas. + atlas); + } else if (strcmp(hbuf, "Cookie") == 0) { + char *p, *eval; + int pl; + + eval = pval + hctx.vlen; + /** + * The cookie value, if it exists, is located between the current header's + * value position and the next one + */ + if (extract_cookie_value(pval, eval, global.deviceatlas.cookiename, + global.deviceatlas.cookienamelen, 1, &p, &pl) == NULL) { + continue; + } + + vlen = (size_t)pl; + pval = p; + evid = da_atlas_clientprop_evidence_id(&global.deviceatlas.atlas); + } else { + evid = da_atlas_header_evidence_id(&global.deviceatlas.atlas, + hbuf); + } + + if (evid == -1) { + continue; + } + + i = vlen > sizeof(vbuf[nbh]) ? sizeof(vbuf[nbh]) : vlen; + memcpy(vbuf[nbh], pval, i - 1); + vbuf[nbh][i - 1] = 0; + ev[nbh].key = evid; + ev[nbh].value = vbuf[nbh]; + ev[nbh].value[vlen] = 0; + ++ nbh; + } + + status = da_searchv(&global.deviceatlas.atlas, &devinfo, + ev, nbh); + + return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo); +} + static struct cfg_kw_list dacfg_kws = {{ }, { { CFG_GLOBAL, "deviceatlas-json-file", da_json_file }, { CFG_GLOBAL, "deviceatlas-log-level", da_log_level }, { CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator }, + { CFG_GLOBAL, "deviceatlas-properties-cookie", da_properties_cookie }, { 0, NULL, NULL }, }}; +/* Note: must not be declared as its list will be overwritten */ +static struct sample_fetch_kw_list fetch_kws = {ILH, { + { "da-csv-fetch", da_haproxy_fetch, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV }, + { NULL, NULL, 0, 0, 0 }, +}}; + /* Note: must not be declared as its list will be overwritten */ static struct sample_conv_kw_list conv_kws = {ILH, { - { "da-csv", da_haproxy, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR }, + { "da-csv-conv", da_haproxy_conv, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR }, { NULL, NULL, 0, 0, 0 }, }}; @@ -234,6 +361,7 @@ __attribute__((constructor)) static void __da_init(void) { /* register sample fetch and format conversion keywords */ + sample_register_fetches(&fetch_kws); sample_register_convs(&conv_kws); cfg_register_keywords(&dacfg_kws); }