MINOR: checks: add rbinary expect match type

The rbinary match works similarly to the rstring match type, however the
received data is rewritten as hex-string before the match operation is
done.

This allows using regexes on binary content even with the POSIX regex
engine.

[Cf: I slightly updated the patch. mem2hex function was removed and dump_binary
is used instead.]
This commit is contained in:
Gaetan Rivet 2020-02-07 15:37:17 +01:00 committed by Christopher Faulet
parent 21f3695126
commit efab6c61d9
4 changed files with 32 additions and 9 deletions

View File

@ -9865,8 +9865,8 @@ tcp-check expect [min-recv <int>] [!] <match> <pattern>
the evaluation result is always conclusive. the evaluation result is always conclusive.
<match> is a keyword indicating how to look for a specific pattern in the <match> is a keyword indicating how to look for a specific pattern in the
response. The keyword may be one of "string", "rstring" or response. The keyword may be one of "string", "rstring", "binary" or
binary. "rbinary".
The keyword may be preceded by an exclamation mark ("!") to negate The keyword may be preceded by an exclamation mark ("!") to negate
the match. Spaces are allowed between the exclamation mark and the the match. Spaces are allowed between the exclamation mark and the
keyword. See below for more details on the supported keywords. keyword. See below for more details on the supported keywords.
@ -9904,6 +9904,15 @@ tcp-check expect [min-recv <int>] [!] <match> <pattern>
this exact hexadecimal string. this exact hexadecimal string.
Purpose is to match data on binary protocols. Purpose is to match data on binary protocols.
rbinary <regex> : test a regular expression on the response buffer, like
"rstring". However, the response buffer is transformed
into its hexadecimal form, including NUL-bytes. This
allows using all regex engines to match any binary
content. The hexadecimal transformation takes twice the
size of the original response. As such, the expected
pattern should work on at-most half the response buffer
size.
It is important to note that the responses will be limited to a certain size It is important to note that the responses will be limited to a certain size
defined by the global "tune.chksize" option, which defaults to 16384 bytes. defined by the global "tune.chksize" option, which defaults to 16384 bytes.
Thus, too large responses may not contain the mandatory pattern when using Thus, too large responses may not contain the mandatory pattern when using

View File

@ -215,6 +215,7 @@ enum tcpcheck_expect_type {
TCPCHK_EXPECT_UNDEF = 0, /* Match is not used. */ TCPCHK_EXPECT_UNDEF = 0, /* Match is not used. */
TCPCHK_EXPECT_STRING, /* Matches a string. */ TCPCHK_EXPECT_STRING, /* Matches a string. */
TCPCHK_EXPECT_REGEX, /* Matches a regular pattern. */ TCPCHK_EXPECT_REGEX, /* Matches a regular pattern. */
TCPCHK_EXPECT_REGEX_BINARY, /* Matches a regular pattern on a hex-encoded text. */
TCPCHK_EXPECT_BINARY, /* Matches a binary sequence. */ TCPCHK_EXPECT_BINARY, /* Matches a binary sequence. */
}; };

View File

@ -3282,7 +3282,8 @@ stats_error_parsing:
expect->string = strdup(args[cur_arg + 1]); expect->string = strdup(args[cur_arg + 1]);
expect->length = strlen(expect->string); expect->length = strlen(expect->string);
} }
else if (strcmp(ptr_arg, "rstring") == 0) { else if (strcmp(ptr_arg, "rstring") == 0 ||
strcmp(ptr_arg, "rbinary") == 0) {
if (!*(args[cur_arg + 1])) { if (!*(args[cur_arg + 1])) {
ha_alert("parsing [%s:%d] : '%s %s %s' expects <regex> as an argument.\n", ha_alert("parsing [%s:%d] : '%s %s %s' expects <regex> as an argument.\n",
file, linenum, args[0], args[1], ptr_arg); file, linenum, args[0], args[1], ptr_arg);
@ -3290,8 +3291,7 @@ stats_error_parsing:
goto out; goto out;
} }
expect->type = TCPCHK_EXPECT_REGEX; expect->type = ((strcmp(ptr_arg, "rbinary") == 0) ? TCPCHK_EXPECT_REGEX_BINARY : TCPCHK_EXPECT_REGEX);
error = NULL; error = NULL;
if (!(expect->regex = regex_comp(args[cur_arg + 1], 1, 1, &error))) { if (!(expect->regex = regex_comp(args[cur_arg + 1], 1, 1, &error))) {
ha_alert("parsing [%s:%d] : '%s %s %s' : regular expression '%s': %s.\n", ha_alert("parsing [%s:%d] : '%s %s %s' : regular expression '%s': %s.\n",
@ -3302,7 +3302,7 @@ stats_error_parsing:
} }
} }
else { else {
ha_alert("parsing [%s:%d] : '%s %s' only supports [!] 'binary', 'string', 'rstring', found '%s'.\n", ha_alert("parsing [%s:%d] : '%s %s' only supports [!] 'binary', 'string', 'rstring', 'rbinary', found '%s'.\n",
file, linenum, args[0], args[1], ptr_arg); file, linenum, args[0], args[1], ptr_arg);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
goto out; goto out;

View File

@ -652,6 +652,9 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
case TCPCHK_EXPECT_REGEX: case TCPCHK_EXPECT_REGEX:
chunk_appendf(chk, " (expect regex)"); chunk_appendf(chk, " (expect regex)");
break; break;
case TCPCHK_EXPECT_REGEX_BINARY:
chunk_appendf(chk, " (expect binary regex)");
break;
case TCPCHK_EXPECT_UNDEF: case TCPCHK_EXPECT_UNDEF:
chunk_appendf(chk, " (undefined expect!)"); chunk_appendf(chk, " (undefined expect!)");
break; break;
@ -2775,7 +2778,6 @@ static char * tcpcheck_get_step_comment(struct check *check, int stepid)
*/ */
static int tcpcheck_main(struct check *check) static int tcpcheck_main(struct check *check)
{ {
char *comment;
struct tcpcheck_rule *next; struct tcpcheck_rule *next;
int done = 0, ret = 0, step = 0; int done = 0, ret = 0, step = 0;
struct conn_stream *cs = check->cs; struct conn_stream *cs = check->cs;
@ -2784,6 +2786,7 @@ static int tcpcheck_main(struct check *check)
struct proxy *proxy = check->proxy; struct proxy *proxy = check->proxy;
struct task *t = check->task; struct task *t = check->task;
struct list *head = check->tcpcheck_rules; struct list *head = check->tcpcheck_rules;
char *comment;
int retcode = 0; int retcode = 0;
/* here, we know that the check is complete or that it failed */ /* here, we know that the check is complete or that it failed */
@ -3064,8 +3067,7 @@ static int tcpcheck_main(struct check *check)
check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list); check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
/* bypass all comment rules */ /* bypass all comment rules */
while (&check->current_step->list != head && while (&check->current_step->list != head && check->current_step->action == TCPCHK_ACT_COMMENT)
check->current_step->action == TCPCHK_ACT_COMMENT)
check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list); check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
if (&check->current_step->list == head) if (&check->current_step->list == head)
@ -3189,6 +3191,12 @@ static int tcpcheck_main(struct check *check)
case TCPCHK_EXPECT_REGEX: case TCPCHK_EXPECT_REGEX:
match = regex_exec2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1)); match = regex_exec2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1));
break; break;
case TCPCHK_EXPECT_REGEX_BINARY:
chunk_reset(&trash);
dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
break;
case TCPCHK_EXPECT_UNDEF: case TCPCHK_EXPECT_UNDEF:
/* Should never happen. */ /* Should never happen. */
retcode = -1; retcode = -1;
@ -3239,6 +3247,10 @@ static int tcpcheck_main(struct check *check)
chunk_printf(&trash, "TCPCHK %s (regex) at step %d", chunk_printf(&trash, "TCPCHK %s (regex) at step %d",
diag, step); diag, step);
break; break;
case TCPCHK_EXPECT_REGEX_BINARY:
chunk_printf(&trash, "TCPCHK %s (binary regex) at step %d",
diag, step);
break;
case TCPCHK_EXPECT_UNDEF: case TCPCHK_EXPECT_UNDEF:
/* Should never happen. */ /* Should never happen. */
retcode = -1; retcode = -1;
@ -3356,6 +3368,7 @@ void email_alert_free(struct email_alert *alert)
free(rule->expect.string); free(rule->expect.string);
break; break;
case TCPCHK_EXPECT_REGEX: case TCPCHK_EXPECT_REGEX:
case TCPCHK_EXPECT_REGEX_BINARY:
regex_free(rule->expect.regex); regex_free(rule->expect.regex);
break; break;
case TCPCHK_EXPECT_UNDEF: case TCPCHK_EXPECT_UNDEF: