"Fossies" - the Fresh Open Source Software Archive

Member "haproxy-2.0.0/src/da.c" (16 Jun 2019, 13024 Bytes) of package /linux/misc/haproxy-2.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "da.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.9.8_vs_2.0.0.

    1 #include <stdio.h>
    2 
    3 #include <common/cfgparse.h>
    4 #include <common/errors.h>
    5 #include <common/http.h>
    6 #include <common/initcall.h>
    7 #include <types/global.h>
    8 #include <proto/arg.h>
    9 #include <proto/http_fetch.h>
   10 #include <proto/http_htx.h>
   11 #include <proto/log.h>
   12 #include <proto/proto_http.h>
   13 #include <proto/sample.h>
   14 #include <dac.h>
   15 
   16 static struct {
   17     void *atlasimgptr;
   18     char *jsonpath;
   19     char *cookiename;
   20     size_t cookienamelen;
   21     da_atlas_t atlas;
   22     da_evidence_id_t useragentid;
   23     da_severity_t loglevel;
   24     char separator;
   25     unsigned char daset:1;
   26 } global_deviceatlas = {
   27     .loglevel = 0,
   28     .jsonpath = 0,
   29     .cookiename = 0,
   30     .cookienamelen = 0,
   31     .useragentid = 0,
   32     .daset = 0,
   33     .separator = '|',
   34 };
   35 
   36 static int da_json_file(char **args, int section_type, struct proxy *curpx,
   37                         struct proxy *defpx, const char *file, int line,
   38                         char **err)
   39 {
   40     if (*(args[1]) == 0) {
   41         memprintf(err, "deviceatlas json file : expects a json path.\n");
   42         return -1;
   43     }
   44     global_deviceatlas.jsonpath = strdup(args[1]);
   45     return 0;
   46 }
   47 
   48 static int da_log_level(char **args, int section_type, struct proxy *curpx,
   49                         struct proxy *defpx, const char *file, int line,
   50                         char **err)
   51 {
   52     int loglevel;
   53     if (*(args[1]) == 0) {
   54         memprintf(err, "deviceatlas log level : expects an integer argument.\n");
   55         return -1;
   56     }
   57 
   58     loglevel = atol(args[1]);
   59     if (loglevel < 0 || loglevel > 3) {
   60         memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]);
   61     } else {
   62         global_deviceatlas.loglevel = (da_severity_t)loglevel;
   63     }
   64 
   65     return 0;
   66 }
   67 
   68 static int da_property_separator(char **args, int section_type, struct proxy *curpx,
   69                                  struct proxy *defpx, const char *file, int line,
   70                                  char **err)
   71 {
   72     if (*(args[1]) == 0) {
   73         memprintf(err, "deviceatlas property separator : expects a character argument.\n");
   74         return -1;
   75     }
   76     global_deviceatlas.separator = *args[1];
   77     return 0;
   78 }
   79 
   80 static int da_properties_cookie(char **args, int section_type, struct proxy *curpx,
   81                           struct proxy *defpx, const char *file, int line,
   82                           char **err)
   83 {
   84     if (*(args[1]) == 0) {
   85         memprintf(err, "deviceatlas cookie name : expects a string argument.\n");
   86         return -1;
   87     } else {
   88         global_deviceatlas.cookiename = strdup(args[1]);
   89     }
   90     global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
   91     return 0;
   92 }
   93 
   94 static size_t da_haproxy_read(void *ctx, size_t len, char *buf)
   95 {
   96     return fread(buf, 1, len, ctx);
   97 }
   98 
   99 static da_status_t da_haproxy_seek(void *ctx, off_t off)
  100 {
  101     return fseek(ctx, off, SEEK_SET) != -1 ? DA_OK : DA_SYS;
  102 }
  103 
  104 static void da_haproxy_log(da_severity_t severity, da_status_t status,
  105     const char *fmt, va_list args)
  106 {
  107     if (global_deviceatlas.loglevel && severity <= global_deviceatlas.loglevel) {
  108         char logbuf[256];
  109         vsnprintf(logbuf, sizeof(logbuf), fmt, args);
  110         ha_warning("deviceatlas : %s.\n", logbuf);
  111     }
  112 }
  113 
  114 #define DA_COOKIENAME_DEFAULT       "DAPROPS"
  115 
  116 /*
  117  * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
  118  */
  119 static int init_deviceatlas(void)
  120 {
  121     int err_code = 0;
  122 
  123     if (global_deviceatlas.jsonpath != 0) {
  124         FILE *jsonp;
  125         da_property_decl_t extraprops[] = {{0, 0}};
  126         size_t atlasimglen;
  127         da_status_t status;
  128 
  129         jsonp = fopen(global_deviceatlas.jsonpath, "r");
  130         if (jsonp == 0) {
  131             ha_alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
  132                  global_deviceatlas.jsonpath);
  133             err_code |= ERR_ALERT | ERR_FATAL;
  134             goto out;
  135         }
  136 
  137         da_init();
  138         da_seterrorfunc(da_haproxy_log);
  139         status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
  140             &global_deviceatlas.atlasimgptr, &atlasimglen);
  141         fclose(jsonp);
  142         if (status != DA_OK) {
  143             ha_alert("deviceatlas : '%s' json file is invalid.\n",
  144                  global_deviceatlas.jsonpath);
  145             err_code |= ERR_ALERT | ERR_FATAL;
  146             goto out;
  147         }
  148 
  149         status = da_atlas_open(&global_deviceatlas.atlas, extraprops,
  150             global_deviceatlas.atlasimgptr, atlasimglen);
  151 
  152         if (status != DA_OK) {
  153             ha_alert("deviceatlas : data could not be compiled.\n");
  154             err_code |= ERR_ALERT | ERR_FATAL;
  155             goto out;
  156         }
  157 
  158         if (global_deviceatlas.cookiename == 0) {
  159             global_deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT);
  160             global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
  161         }
  162 
  163         global_deviceatlas.useragentid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
  164             "user-agent");
  165         global_deviceatlas.daset = 1;
  166 
  167         fprintf(stdout, "Deviceatlas module loaded.\n");
  168     }
  169 
  170 out:
  171     return err_code;
  172 }
  173 
  174 static void deinit_deviceatlas(void)
  175 {
  176     if (global_deviceatlas.jsonpath != 0) {
  177         free(global_deviceatlas.jsonpath);
  178     }
  179 
  180     if (global_deviceatlas.daset == 1) {
  181         free(global_deviceatlas.cookiename);
  182         da_atlas_close(&global_deviceatlas.atlas);
  183         free(global_deviceatlas.atlasimgptr);
  184     }
  185 
  186     da_fini();
  187 }
  188 
  189 static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo)
  190 {
  191     struct buffer *tmp;
  192     da_propid_t prop, *pprop;
  193     da_status_t status;
  194     da_type_t proptype;
  195     const char *propname;
  196     int i;
  197 
  198     tmp = get_trash_chunk();
  199     chunk_reset(tmp);
  200 
  201     propname = (const char *) args[0].data.str.area;
  202     i = 0;
  203 
  204     for (; propname != 0; i ++,
  205          propname = (const char *) args[i].data.str.area) {
  206         status = da_atlas_getpropid(&global_deviceatlas.atlas,
  207             propname, &prop);
  208         if (status != DA_OK) {
  209             chunk_appendf(tmp, "%c", global_deviceatlas.separator);
  210             continue;
  211         }
  212         pprop = &prop;
  213         da_atlas_getproptype(&global_deviceatlas.atlas, *pprop, &proptype);
  214 
  215         switch (proptype) {
  216             case DA_TYPE_BOOLEAN: {
  217                 bool val;
  218                 status = da_getpropboolean(devinfo, *pprop, &val);
  219                 if (status == DA_OK) {
  220                     chunk_appendf(tmp, "%d", val);
  221                 }
  222                 break;
  223             }
  224             case DA_TYPE_INTEGER:
  225             case DA_TYPE_NUMBER: {
  226                 long val;
  227                 status = da_getpropinteger(devinfo, *pprop, &val);
  228                 if (status == DA_OK) {
  229                     chunk_appendf(tmp, "%ld", val);
  230                 }
  231                 break;
  232             }
  233             case DA_TYPE_STRING: {
  234                 const char *val;
  235                 status = da_getpropstring(devinfo, *pprop, &val);
  236                 if (status == DA_OK) {
  237                     chunk_appendf(tmp, "%s", val);
  238                 }
  239                 break;
  240                 }
  241             default:
  242             break;
  243         }
  244 
  245         chunk_appendf(tmp, "%c", global_deviceatlas.separator);
  246     }
  247 
  248     da_close(devinfo);
  249 
  250     if (tmp->data) {
  251         --tmp->data;
  252         tmp->area[tmp->data] = 0;
  253     }
  254 
  255     smp->data.u.str.area = tmp->area;
  256     smp->data.u.str.data = tmp->data;
  257 
  258     return 1;
  259 }
  260 
  261 static int da_haproxy_conv(const struct arg *args, struct sample *smp, void *private)
  262 {
  263     da_deviceinfo_t devinfo;
  264     da_status_t status;
  265     const char *useragent;
  266     char useragentbuf[1024] = { 0 };
  267     int i;
  268 
  269     if (global_deviceatlas.daset == 0 || smp->data.u.str.data == 0) {
  270         return 1;
  271     }
  272 
  273     i = smp->data.u.str.data > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.data;
  274     memcpy(useragentbuf, smp->data.u.str.area, i - 1);
  275     useragentbuf[i - 1] = 0;
  276 
  277     useragent = (const char *)useragentbuf;
  278 
  279     status = da_search(&global_deviceatlas.atlas, &devinfo,
  280         global_deviceatlas.useragentid, useragent, 0);
  281 
  282     return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
  283 }
  284 
  285 #define DA_MAX_HEADERS       24
  286 
  287 static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
  288 {
  289     da_evidence_t ev[DA_MAX_HEADERS];
  290     da_deviceinfo_t devinfo;
  291     da_status_t status;
  292     struct channel *chn;
  293     char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }};
  294     int i, nbh = 0;
  295 
  296     if (global_deviceatlas.daset == 0) {
  297         return 0;
  298     }
  299 
  300     chn = (smp->strm ? &smp->strm->req : NULL);
  301     /* HTX Mode check */
  302     if (smp->px->options2 & PR_O2_USE_HTX) {
  303         struct htx_blk *blk;
  304         struct htx *htx = smp_prefetch_htx(smp, chn, 1);
  305         if (!htx) {
  306             return 0;
  307         }
  308 
  309         i = 0;
  310         for (blk = htx_get_first_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) {
  311             size_t vlen;
  312             char *pval;
  313             da_evidence_id_t evid;
  314             enum htx_blk_type type;
  315             struct ist n, v;
  316             char hbuf[24] = { 0 };
  317             char tval[1024] = { 0 };
  318 
  319             type = htx_get_blk_type(blk);
  320 
  321             if (type == HTX_BLK_HDR) {
  322                 n = htx_get_blk_name(htx, blk);
  323                 v = htx_get_blk_value(htx, blk);
  324             } else if (type == HTX_BLK_EOH) {
  325                 break;
  326             } else {
  327                 continue;
  328             }
  329 
  330             /* The HTTP headers used by the DeviceAtlas API are not longer */
  331             if (n.len >= sizeof(hbuf)) {
  332                 continue;
  333             }
  334 
  335             memcpy(hbuf, n.ptr, n.len);
  336             hbuf[n.len] = 0;
  337             pval = v.ptr;
  338             vlen = v.len;
  339             evid = -1;
  340             i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len;
  341             memcpy(tval, v.ptr, i);
  342             tval[i] = 0;
  343             pval = tval;
  344 
  345             if (strcasecmp(hbuf, "Accept-Language") == 0) {
  346                 evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas);
  347             } else if (strcasecmp(hbuf, "Cookie") == 0) {
  348                 char *p, *eval;
  349                 size_t pl;
  350 
  351                 eval = pval + vlen;
  352                 /**
  353                  * The cookie value, if it exists, is located between the current header's
  354                  * value position and the next one
  355                  */
  356                 if (http_extract_cookie_value(pval, eval, global_deviceatlas.cookiename,
  357                             global_deviceatlas.cookienamelen, 1, &p, &pl) == NULL) {
  358                     continue;
  359                 }
  360 
  361                 vlen -= global_deviceatlas.cookienamelen - 1;
  362                 pval = p;
  363                 evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
  364             } else {
  365                 evid = da_atlas_header_evidence_id(&global_deviceatlas.atlas, hbuf);
  366             }
  367 
  368             if (evid == -1) {
  369                 continue;
  370             }
  371 
  372             i = vlen > sizeof(vbuf[nbh]) - 1 ? sizeof(vbuf[nbh]) - 1 : vlen;
  373             memcpy(vbuf[nbh], pval, i);
  374             vbuf[nbh][i] = 0;
  375             ev[nbh].key = evid;
  376             ev[nbh].value = vbuf[nbh];
  377             ++ nbh;
  378         }
  379     } else {
  380         struct hdr_idx *hidx;
  381         struct hdr_ctx hctx;
  382         const struct http_msg *hmsg;
  383         CHECK_HTTP_MESSAGE_FIRST(chn);
  384         smp->data.type = SMP_T_STR;
  385 
  386         /**
  387          * Here we go through the whole list of headers from start
  388          * they will be filtered via the DeviceAtlas API itself
  389          */
  390         hctx.idx = 0;
  391         hidx = &smp->strm->txn->hdr_idx;
  392         hmsg = &smp->strm->txn->req;
  393 
  394         while (http_find_next_header(ci_head(hmsg->chn), hidx, &hctx) == 1 &&
  395                 nbh < DA_MAX_HEADERS) {
  396             char *pval;
  397             size_t vlen;
  398             da_evidence_id_t evid = -1;
  399             char hbuf[24] = { 0 };
  400 
  401             /* The HTTP headers used by the DeviceAtlas API are not longer */
  402             if (hctx.del >= sizeof(hbuf) || hctx.del <= 0 || hctx.vlen <= 0) {
  403                 continue;
  404             }
  405 
  406             vlen = hctx.vlen;
  407             memcpy(hbuf, hctx.line, hctx.del);
  408             hbuf[hctx.del] = 0;
  409             pval = (hctx.line + hctx.val);
  410 
  411             if (strcmp(hbuf, "Accept-Language") == 0) {
  412                 evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.
  413                         atlas);
  414             } else if (strcmp(hbuf, "Cookie") == 0) {
  415                 char *p, *eval;
  416                 size_t pl;
  417 
  418                 eval = pval + hctx.vlen;
  419                 /**
  420                  * The cookie value, if it exists, is located between the current header's
  421                  * value position and the next one
  422                  */
  423                 if (http_extract_cookie_value(pval, eval, global_deviceatlas.cookiename,
  424                             global_deviceatlas.cookienamelen, 1, &p, &pl) == NULL) {
  425                     continue;
  426                 }
  427 
  428                 vlen = (size_t)pl;
  429                 pval = p;
  430                 evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
  431             } else {
  432                 evid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
  433                         hbuf);
  434             }
  435 
  436             if (evid == -1) {
  437                 continue;
  438             }
  439 
  440             i = vlen > sizeof(vbuf[nbh]) ? sizeof(vbuf[nbh]) : vlen;
  441             memcpy(vbuf[nbh], pval, i - 1);
  442             vbuf[nbh][i - 1] = 0;
  443             ev[nbh].key = evid;
  444             ev[nbh].value = vbuf[nbh];
  445             ++ nbh;
  446         }
  447     }
  448 
  449     status = da_searchv(&global_deviceatlas.atlas, &devinfo,
  450             ev, nbh);
  451 
  452     return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
  453 }
  454 
  455 static struct cfg_kw_list dacfg_kws = {{ }, {
  456     { CFG_GLOBAL, "deviceatlas-json-file",    da_json_file },
  457         { CFG_GLOBAL, "deviceatlas-log-level",    da_log_level },
  458         { CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator },
  459         { CFG_GLOBAL, "deviceatlas-properties-cookie", da_properties_cookie },
  460         { 0, NULL, NULL },
  461 }};
  462 
  463 INITCALL1(STG_REGISTER, cfg_register_keywords, &dacfg_kws);
  464 
  465 /* Note: must not be declared <const> as its list will be overwritten */
  466 static struct sample_fetch_kw_list fetch_kws = {ILH, {
  467     { "da-csv-fetch", da_haproxy_fetch, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
  468         { NULL, NULL, 0, 0, 0 },
  469 }};
  470 
  471 INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws);
  472 
  473 /* Note: must not be declared <const> as its list will be overwritten */
  474 static struct sample_conv_kw_list conv_kws = {ILH, {
  475     { "da-csv-conv", da_haproxy_conv, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR },
  476         { NULL, NULL, 0, 0, 0 },
  477 }};
  478 
  479 static void da_haproxy_register_build_options()
  480 {
  481     char *ptr = NULL;
  482 
  483 #ifdef MOBI_DA_DUMMY_LIBRARY
  484     memprintf(&ptr, "Built with DeviceAtlas support (dummy library only).");
  485 #else
  486     memprintf(&ptr, "Built with DeviceAtlas support (library version %u.%u).", MOBI_DA_MAJOR, MOBI_DA_MINOR);
  487 #endif
  488     hap_register_build_opts(ptr, 1);
  489 }
  490 
  491 INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
  492 
  493 REGISTER_POST_CHECK(init_deviceatlas);
  494 REGISTER_POST_DEINIT(deinit_deviceatlas);
  495 INITCALL0(STG_REGISTER, da_haproxy_register_build_options);