MEDIUM: checks: Support log-format strings for tcp-check send rules

An extra parameter for tcp-check send rules can be specified to handle the
string or the hexa string as a log-format one. Using "log-format" option,
instead of considering the data to send as raw data, it is parsed as a
log-format string. Thus it is possible to call sample fetches to customize data
sent to a server. Of course, because we have no stream attached to healthchecks,
not all sample fetches are available. So be careful.

    tcp-check set-var(check.port) int(8000)
    tcp-check set-var(check.uri) str(/status)
    tcp-check connect port var(check.port)
    tcp-check send "GET %[check.uri] HTTP/1.0\r\n" log-format
    tcp-check send "Host: %[srv_name]\r\n" log-format
    tcp-check send "\r\n"
This commit is contained in:
Christopher Faulet 2020-03-30 19:52:29 +02:00
parent b7d30098f3
commit f50f4e956f
2 changed files with 119 additions and 50 deletions

View File

@ -230,15 +230,19 @@ struct tcpcheck_connect {
}; };
enum tcpcheck_send_type { enum tcpcheck_send_type {
TCPCHK_SEND_UNDEF = 0, /* Send is not parsed. */ TCPCHK_SEND_UNDEF = 0, /* Send is not parsed. */
TCPCHK_SEND_STRING, /* Send an ASCII string. */ TCPCHK_SEND_STRING, /* Send an ASCII string. */
TCPCHK_SEND_BINARY, /* Send a binary sequence. */ TCPCHK_SEND_BINARY, /* Send a binary sequence. */
TCPCHK_SEND_STRING_LF, /* Send an ASCII log-format string. */
TCPCHK_SEND_BINARY_LF, /* Send a binary log-format sequence. */
}; };
struct tcpcheck_send { struct tcpcheck_send {
enum tcpcheck_send_type type; enum tcpcheck_send_type type;
char *string; /* Sending an ASCII string or a binary sequence. */ union {
int length; /* Size in bytes of the sequence referenced by string / binary. */ struct ist data; /* an ASCII string or a binary sequence */
struct list fmt; /* an ASCII or hexa log-format string */
};
}; };
enum tcpcheck_expect_type { enum tcpcheck_expect_type {

View File

@ -3028,24 +3028,41 @@ static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcp
struct tcpcheck_send *send = &rule->send; struct tcpcheck_send *send = &rule->send;
struct conn_stream *cs = check->cs; struct conn_stream *cs = check->cs;
struct connection *conn = cs_conn(cs); struct connection *conn = cs_conn(cs);
struct buffer *tmp = NULL;
/* reset the read & write buffer */ /* reset the read & write buffer */
b_reset(&check->bi); b_reset(&check->bi);
b_reset(&check->bo); b_reset(&check->bo);
if (send->length >= b_size(&check->bo)) {
chunk_printf(&trash, "tcp-check send : string too large (%d) for buffer size (%u) at step %d",
send->length, (unsigned int)b_size(&check->bo),
tcpcheck_get_step_id(check, rule));
set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
ret = TCPCHK_EVAL_STOP;
goto out;
}
switch (send->type) { switch (send->type) {
case TCPCHK_SEND_STRING: case TCPCHK_SEND_STRING:
case TCPCHK_SEND_BINARY: case TCPCHK_SEND_BINARY:
b_putblk(&check->bo, send->string, send->length); if (istlen(send->data) >= b_size(&check->bo)) {
chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
(unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
tcpcheck_get_step_id(check, rule));
set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
ret = TCPCHK_EVAL_STOP;
goto out;
}
b_putist(&check->bo, send->data);
break;
case TCPCHK_SEND_STRING_LF:
check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
if (!b_data(&check->bo))
goto out;
break;
case TCPCHK_SEND_BINARY_LF:
tmp = alloc_trash_chunk();
if (!tmp)
goto error_lf;
tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
if (!b_data(tmp))
goto out;
tmp->area[tmp->data] = '\0';
b_set_data(&check->bo, b_size(&check->bo));
if (parse_binary(b_orig(tmp), &check->bo.area, (int *)&check->bo.data, NULL) == 0)
goto error_lf;
break; break;
case TCPCHK_SEND_UNDEF: case TCPCHK_SEND_UNDEF:
/* Should never happen. */ /* Should never happen. */
@ -3066,7 +3083,16 @@ static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcp
} }
out: out:
free_trash_chunk(tmp);
return ret; return ret;
error_lf:
chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
tcpcheck_get_step_id(check, rule));
set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
ret = TCPCHK_EVAL_STOP;
goto out;
} }
/* Evaluate a TCPCHK_ACT_EXPECT rule. It returns 1 to evaluate the next rule, 0 /* Evaluate a TCPCHK_ACT_EXPECT rule. It returns 1 to evaluate the next rule, 0
@ -3443,6 +3469,8 @@ void free_check(struct check *check)
static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool) static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
{ {
struct logformat_node *lf, *lfb;
if (!rule) if (!rule)
return; return;
@ -3452,7 +3480,16 @@ static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
switch (rule->send.type) { switch (rule->send.type) {
case TCPCHK_SEND_STRING: case TCPCHK_SEND_STRING:
case TCPCHK_SEND_BINARY: case TCPCHK_SEND_BINARY:
free(rule->send.string); free(rule->send.data.ptr);
break;
case TCPCHK_SEND_STRING_LF:
case TCPCHK_SEND_BINARY_LF:
list_for_each_entry_safe(lf, lfb, &rule->send.fmt, list) {
LIST_DEL(&lf->list);
release_sample_expr(lf->expr);
free(lf->arg);
free(lf);
}
break; break;
case TCPCHK_SEND_UNDEF: case TCPCHK_SEND_UNDEF:
break; break;
@ -3669,15 +3706,15 @@ static int add_tcpcheck_send_strs(struct list *list, const char * const *strs)
send->type = TCPCHK_SEND_STRING; send->type = TCPCHK_SEND_STRING;
for (i = 0; strs[i]; i++) for (i = 0; strs[i]; i++)
send->length += strlen(strs[i]); send->data.len += strlen(strs[i]);
send->string = malloc(send->length + 1); send->data.ptr = malloc(send->data.len + 1);
if (!send->string) { if (!isttest(send->data)) {
pool_free(pool_head_tcpcheck_rule, tcpcheck); pool_free(pool_head_tcpcheck_rule, tcpcheck);
return 0; return 0;
} }
dst = send->string; dst = send->data.ptr;
for (i = 0; strs[i]; i++) for (i = 0; strs[i]; i++)
for (in = strs[i]; (*dst = *in++); dst++); for (in = strs[i]; (*dst = *in++); dst++);
*dst = 0; *dst = 0;
@ -4283,12 +4320,12 @@ static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, st
return NULL; return NULL;
} }
static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct list *rules, char **errmsg) static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
char **errmsg)
{ {
struct tcpcheck_rule *chk = NULL; struct tcpcheck_rule *chk = NULL;
char *str = NULL, *comment = NULL; char *comment = NULL, *data = NULL;
enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF; enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
int len;
type = ((strcmp(args[cur_arg], "send-binary") == 0) ? TCPCHK_SEND_BINARY : TCPCHK_SEND_STRING); type = ((strcmp(args[cur_arg], "send-binary") == 0) ? TCPCHK_SEND_BINARY : TCPCHK_SEND_STRING);
if (!*(args[cur_arg+1])) { if (!*(args[cur_arg+1])) {
@ -4297,33 +4334,35 @@ static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struc
goto error; goto error;
} }
if (type == TCPCHK_SEND_BINARY) { data = args[cur_arg+1];
if (parse_binary(args[cur_arg+1], &str, &len, errmsg) == 0) {
memprintf(errmsg, "'%s' invalid binary string (%s).\n", args[cur_arg], *errmsg);
goto error;
}
}
else {
str = strdup(args[cur_arg+1]);
len = strlen(args[cur_arg+1]);
if (!str) {
memprintf(errmsg, "out of memory");
goto error;
}
}
cur_arg++;
if (strcmp(args[cur_arg], "comment") == 0) { cur_arg += 2;
if (!*(args[cur_arg+1])) { while (*(args[cur_arg])) {
memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]); if (strcmp(args[cur_arg], "comment") == 0) {
if (!*(args[cur_arg+1])) {
memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
goto error;
}
cur_arg++;
free(comment);
comment = strdup(args[cur_arg]);
if (!comment) {
memprintf(errmsg, "out of memory");
goto error;
}
}
else if (strcmp(args[cur_arg], "log-format") == 0) {
if (type == TCPCHK_SEND_BINARY)
type = TCPCHK_SEND_BINARY_LF;
else if (type == TCPCHK_SEND_STRING)
type = TCPCHK_SEND_STRING_LF;
}
else {
memprintf(errmsg, "expects 'comment', 'log-format' but got '%s' as argument.",
args[cur_arg]);
goto error; goto error;
} }
cur_arg++; cur_arg++;
comment = strdup(args[cur_arg]);
if (!comment) {
memprintf(errmsg, "out of memory");
goto error;
}
} }
chk = calloc(1, sizeof(*chk)); chk = calloc(1, sizeof(*chk));
@ -4334,12 +4373,38 @@ static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struc
chk->action = TCPCHK_ACT_SEND; chk->action = TCPCHK_ACT_SEND;
chk->comment = comment; chk->comment = comment;
chk->send.type = type; chk->send.type = type;
chk->send.string = str;
chk->send.length = len; switch (chk->send.type) {
case TCPCHK_SEND_STRING:
chk->send.data = ist2(strdup(data), strlen(data));
if (!isttest(chk->send.data)) {
memprintf(errmsg, "out of memory");
goto error;
}
break;
case TCPCHK_SEND_BINARY:
if (parse_binary(data, &chk->send.data.ptr, (int *)&chk->send.data.len, errmsg) == 0) {
memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
goto error;
}
break;
case TCPCHK_SEND_STRING_LF:
case TCPCHK_SEND_BINARY_LF:
LIST_INIT(&chk->send.fmt);
px->conf.args.ctx = ARGC_SRV;
if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
goto error;
}
break;
case TCPCHK_SEND_UNDEF:
goto error;
}
return chk; return chk;
error: error:
free(str); free(chk);
free(comment); free(comment);
return NULL; return NULL;
} }
@ -4578,7 +4643,7 @@ static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
if (strcmp(args[cur_arg], "connect") == 0) if (strcmp(args[cur_arg], "connect") == 0)
chk = parse_tcpcheck_connect(args, cur_arg, curpx, rules, file, line, errmsg); chk = parse_tcpcheck_connect(args, cur_arg, curpx, rules, file, line, errmsg);
else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0) else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0)
chk = parse_tcpcheck_send(args, cur_arg, rules, errmsg); chk = parse_tcpcheck_send(args, cur_arg, curpx, rules, errmsg);
else if (strcmp(args[cur_arg], "expect") == 0) else if (strcmp(args[cur_arg], "expect") == 0)
chk = parse_tcpcheck_expect(args, cur_arg, rules, errmsg); chk = parse_tcpcheck_expect(args, cur_arg, rules, errmsg);
else if (strcmp(args[cur_arg], "comment") == 0) else if (strcmp(args[cur_arg], "comment") == 0)