]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tcp: add a new tcp-request capture directive
authorWilly Tarreau <w@1wt.eu>
Fri, 13 Jun 2014 14:18:52 +0000 (16:18 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 13 Jun 2014 14:45:53 +0000 (16:45 +0200)
This new directive captures the specified fetch expression, converts
it to text and puts it into the next capture slot. The capture slots
are shared with header captures so that it is possible to dump all
captures at once or selectively in logs and header processing.

The purpose is to permit logs to contain whatever payload is found in
a request, for example bytes at a fixed location or the SNI of forwarded
SSL traffic.

doc/configuration.txt
src/proto_tcp.c

index 89bdf986a5c07a965c59449a89eae479ff3bea5c..43d4760919491d468d5952724321f13af7de1e45 100644 (file)
@@ -7171,7 +7171,7 @@ tcp-request connection <action> [{if | unless} <condition>]
   accept the incoming connection. There is no specific limit to the number of
   rules which may be inserted.
 
-  Three types of actions are supported :
+  Five types of actions are supported :
     - accept :
         accepts the connection if the condition is true (when used with "if")
         or false (when used with "unless"). The first such rule executed ends
@@ -7200,6 +7200,18 @@ tcp-request connection <action> [{if | unless} <condition>]
         of load balancers are passed through by traffic coming from public
         hosts.
 
+    - capture <sample> len <length> :
+        This only applies to "tcp-request content" rules. It captures sample
+        expression <sample> from the request buffer, and converts it to a
+        string of at most <len> characters. The resulting string is stored into
+        the next request "capture" slot, so it will possibly appear next to
+        some captured HTTP headers. It will then automatically appear in the
+        logs, and it will be possible to extract it using sample fetch rules to
+        feed it into headers or anything. The length should be limited given
+        that this size will be allocated for each capture during the whole
+        session life. Since it applies to Please check section 7.3 (Fetching
+        samples) and "capture request header" for more information.
+
     - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] :
         enables tracking of sticky counters from current connection. These
         rules do not stop evaluation and do not change default action. Two sets
@@ -7278,8 +7290,8 @@ tcp-request content <action> [{if | unless} <condition>]
   Arguments :
     <action>    defines the action to perform if the condition applies. Valid
                 actions include : "accept", "reject", "track-sc0", "track-sc1",
-                and "track-sc2". See "tcp-request connection" above for their
-                signification.
+                "track-sc2" and "capture". See "tcp-request connection" above
+                for their signification.
 
     <condition> is a standard layer 4-7 ACL-based condition (see section 7).
 
@@ -7307,9 +7319,10 @@ tcp-request content <action> [{if | unless} <condition>]
   contents. There is no specific limit to the number of rules which may be
   inserted.
 
-  Three types of actions are supported :
-    - accept :
-    - reject :
+  Four types of actions are supported :
+    - accept : the request is accepted
+    - reject : the request is rejected and the connection is closed
+    - capture : the specified sample expression is captured
     - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>]
 
   They have the same meaning as their counter-parts in "tcp-request connection"
index a672de41576754f42f4f3162f845ee3823b24786..65c4fdad379e3af349ced64fa3423b17414f742e 100644 (file)
@@ -35,6 +35,7 @@
 #include <common/standard.h>
 
 #include <types/global.h>
+#include <types/capture.h>
 #include <types/server.h>
 
 #include <proto/acl.h>
@@ -990,13 +991,8 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit)
 
                if (rule->cond) {
                        ret = acl_exec_cond(rule->cond, s->be, s, &s->txn, SMP_OPT_DIR_REQ | partial);
-                       if (ret == ACL_TEST_MISS) {
-                               channel_dont_connect(req);
-                               /* just set the request timeout once at the beginning of the request */
-                               if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
-                                       req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
-                               return 0;
-                       }
+                       if (ret == ACL_TEST_MISS)
+                               goto missing_data;
 
                        ret = acl_pass(ret);
                        if (rule->cond->pol == ACL_COND_UNLESS)
@@ -1040,6 +1036,32 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit)
                                                stkctr_set_flags(&s->stkctr[tcp_trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
                                }
                        }
+                       else if (rule->action == TCP_ACT_CAPTURE) {
+                               struct sample *key;
+                               struct cap_hdr *h = rule->act_prm.cap.hdr;
+                               char **cap = s->txn.req.cap;
+                               int len;
+
+                               key = sample_fetch_string(s->be, s, &s->txn, SMP_OPT_DIR_REQ | partial, rule->act_prm.cap.expr);
+                               if (!key)
+                                       continue;
+
+                               if (key->flags & SMP_F_MAY_CHANGE)
+                                       goto missing_data;
+
+                               if (cap[h->index] == NULL)
+                                       cap[h->index] = pool_alloc2(h->pool);
+
+                               if (cap[h->index] == NULL) /* no more capture memory */
+                                       continue;
+
+                               len = key->data.str.len;
+                               if (len > h->len)
+                                       len = h->len;
+
+                               memcpy(cap[h->index], key->data.str.str, len);
+                               cap[h->index][len] = 0;
+                       }
                        else {
                                /* otherwise accept */
                                break;
@@ -1053,6 +1075,14 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit)
        req->analysers &= ~an_bit;
        req->analyse_exp = TICK_ETERNITY;
        return 1;
+
+ missing_data:
+       channel_dont_connect(req);
+       /* just set the request timeout once at the beginning of the request */
+       if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
+               req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
+       return 0;
+
 }
 
 /* This function performs the TCP response analysis on the current response. It
@@ -1287,6 +1317,92 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
                arg++;
                rule->action = TCP_ACT_REJECT;
        }
+       else if (strcmp(args[arg], "capture") == 0) {
+               struct sample_expr *expr;
+               struct cap_hdr *hdr;
+               int kw = arg;
+               int len = 0;
+
+               if (!(curpx->cap & PR_CAP_FE)) {
+                       memprintf(err,
+                                 "'%s %s %s' : proxy '%s' has no frontend capability",
+                                 args[0], args[1], args[kw], curpx->id);
+                       return -1;
+               }
+
+               if (!(where & SMP_VAL_FE_REQ_CNT)) {
+                       memprintf(err,
+                                 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
+                                 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
+                       return -1;
+               }
+
+               arg++;
+
+               curpx->conf.args.ctx = ARGC_CAP;
+               expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
+               if (!expr) {
+                       memprintf(err,
+                                 "'%s %s %s' : %s",
+                                 args[0], args[1], args[kw], *err);
+                       return -1;
+               }
+
+               if (!(expr->fetch->val & where)) {
+                       memprintf(err,
+                                 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
+                                 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
+                       free(expr);
+                       return -1;
+               }
+
+               if (strcmp(args[arg], "len") == 0) {
+                       arg++;
+                       if (!args[arg]) {
+                               memprintf(err,
+                                         "'%s %s %s' : missing length value",
+                                         args[0], args[1], args[kw]);
+                               free(expr);
+                               return -1;
+                       }
+                       /* we copy the table name for now, it will be resolved later */
+                       len = atoi(args[arg]);
+                       if (len <= 0) {
+                               memprintf(err,
+                                         "'%s %s %s' : length must be > 0",
+                                         args[0], args[1], args[kw]);
+                               free(expr);
+                               return -1;
+                       }
+                       arg++;
+               }
+
+               if (!len) {
+                       memprintf(err,
+                                 "'%s %s %s' : a positive 'len' argument is mandatory",
+                                 args[0], args[1], args[kw]);
+                       free(expr);
+                       return -1;
+               }
+
+               hdr = calloc(sizeof(struct cap_hdr), 1);
+               hdr->next = curpx->req_cap;
+               hdr->name = NULL; /* not a header capture */
+               hdr->namelen = 0;
+               hdr->len = len;
+               hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
+               hdr->index = curpx->nb_req_cap++;
+
+               curpx->req_cap = hdr;
+               curpx->to_log |= LW_REQHDR;
+
+               /* check if we need to allocate an hdr_idx struct for HTTP parsing */
+               curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
+
+               rule->act_prm.cap.expr = expr;
+               rule->act_prm.cap.hdr = hdr;
+               rule->action = TCP_ACT_CAPTURE;
+       }
        else if (strncmp(args[arg], "track-sc", 8) == 0 &&
                 args[arg][9] == '\0' && args[arg][8] >= '0' &&
                 args[arg][8] <= '0' + MAX_SESS_STKCTR) { /* track-sc 0..9 */