MINOR: checks: define a tcp expect type

Extract the expect definition from its tcpcheck ; create a standalone type.
This commit is contained in:
Gaetan Rivet 2020-02-07 15:37:17 +01:00 committed by Christopher Faulet
parent f8ba6773e5
commit b616add793
3 changed files with 139 additions and 62 deletions

View File

@ -211,6 +211,25 @@ struct analyze_status {
unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
};
enum tcpcheck_expect_type {
TCPCHK_EXPECT_UNDEF = 0, /* Match is not used. */
TCPCHK_EXPECT_STRING, /* Matches a string. */
TCPCHK_EXPECT_REGEX, /* Matches a regular pattern. */
TCPCHK_EXPECT_BINARY, /* Matches a binary sequence. */
};
struct tcpcheck_expect {
enum tcpcheck_expect_type type; /* Type of pattern used for matching. */
union {
char *string; /* Matching a literal string / binary anywhere in the response. */
struct my_regex *regex; /* Matching a regex pattern. */
};
struct tcpcheck_rule *head; /* first expect of a chain. */
int length; /* Size in bytes of the pattern referenced by string / binary. */
int inverse; /* Match is inversed. */
int min_recv; /* Minimum amount of data before an expect can be applied. (default: -1, ignored) */
};
/* possible actions for tcpcheck_rule->action */
enum tcpcheck_rule_type {
TCPCHK_ACT_SEND = 0, /* send action, regular string format */
@ -229,16 +248,11 @@ struct tcpcheck_rule {
struct list list; /* list linked to from the proxy */
enum tcpcheck_rule_type action; /* type of the rule. */
char *comment; /* comment to be used in the logs and on the stats socket */
/* match type uses NON-NULL pointer from either string or expect_regex below */
/* sent string is string */
char *string; /* sent or expected string */
int string_len; /* string length */
int min_recv; /* Minimum amount of data before an expect can be applied. (default: -1, ignored) */
struct my_regex *expect_regex; /* expected */
int inverse; /* 0 = regular match, 1 = inverse match */
char *string; /* sent string */
int string_len; /* sent string length */
struct tcpcheck_expect expect; /* Expected pattern. */
unsigned short port; /* port to connect to */
unsigned short conn_opts; /* options when setting up a new connection */
struct tcpcheck_rule *expect_head; /* first expect of a chain. */
};
#endif /* _TYPES_CHECKS_H */

View File

@ -3142,7 +3142,6 @@ stats_error_parsing:
tcpcheck->action = TCPCHK_ACT_SEND;
tcpcheck->string_len = strlen(args[2]);
tcpcheck->string = strdup(args[2]);
tcpcheck->expect_regex = NULL;
/* comment for this tcpcheck line */
if (strcmp(args[3], "comment") == 0) {
@ -3178,7 +3177,6 @@ stats_error_parsing:
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
tcpcheck->expect_regex = NULL;
/* comment for this tcpcheck line */
if (strcmp(args[3], "comment") == 0) {
@ -3196,6 +3194,7 @@ stats_error_parsing:
}
else if (strcmp(args[1], "expect") == 0) {
struct tcpcheck_rule *tcpcheck, *prev_check;
struct tcpcheck_expect *expect;
long min_recv = -1;
const char *ptr_arg;
int cur_arg;
@ -3249,8 +3248,9 @@ stats_error_parsing:
tcpcheck = calloc(1, sizeof(*tcpcheck));
tcpcheck->action = TCPCHK_ACT_EXPECT;
tcpcheck->inverse = inverse;
tcpcheck->min_recv = min_recv;
expect = &tcpcheck->expect;
expect->inverse = inverse;
expect->min_recv = min_recv;
if (strcmp(ptr_arg, "binary") == 0) {
char *err = NULL;
@ -3262,7 +3262,8 @@ stats_error_parsing:
goto out;
}
if (parse_binary(args[cur_arg + 1], &tcpcheck->string, &tcpcheck->string_len, &err) == 0) {
expect->type = TCPCHK_EXPECT_BINARY;
if (parse_binary(args[cur_arg + 1], &expect->string, &expect->length, &err) == 0) {
ha_alert("parsing [%s:%d] : '%s %s %s' expects <BINARY STRING> as argument, but %s\n",
file, linenum, args[0], args[1], args[2], err);
err_code |= ERR_ALERT | ERR_FATAL;
@ -3277,8 +3278,9 @@ stats_error_parsing:
goto out;
}
tcpcheck->string_len = strlen(args[cur_arg + 1]);
tcpcheck->string = strdup(args[cur_arg + 1]);
expect->type = TCPCHK_EXPECT_STRING;
expect->string = strdup(args[cur_arg + 1]);
expect->length = strlen(expect->string);
}
else if (strcmp(ptr_arg, "rstring") == 0) {
if (!*(args[cur_arg + 1])) {
@ -3288,8 +3290,10 @@ stats_error_parsing:
goto out;
}
expect->type = TCPCHK_EXPECT_REGEX;
error = NULL;
if (!(tcpcheck->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",
file, linenum, args[0], args[1], ptr_arg, args[cur_arg + 1], error);
free(error);
@ -3319,11 +3323,11 @@ stats_error_parsing:
/* All tcp-check expect points back to the first inverse expect rule
* in a chain of one or more expect rule, potentially itself.
*/
tcpcheck->expect_head = tcpcheck;
tcpcheck->expect.head = tcpcheck;
list_for_each_entry_rev(prev_check, &curproxy->tcpcheck_rules, list) {
if (prev_check->action == TCPCHK_ACT_EXPECT) {
if (prev_check->inverse)
tcpcheck->expect_head = prev_check;
if (prev_check->expect.inverse)
tcpcheck->expect.head = prev_check;
continue;
}
if (prev_check->action != TCPCHK_ACT_COMMENT)

View File

@ -640,10 +640,22 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
chunk_appendf(chk, " (connect)");
}
else if (check->last_started_step && check->last_started_step->action == TCPCHK_ACT_EXPECT) {
if (check->last_started_step->string)
chunk_appendf(chk, " (expect string '%s')", check->last_started_step->string);
else if (check->last_started_step->expect_regex)
struct tcpcheck_expect *expect = &check->last_started_step->expect;
switch (expect->type) {
case TCPCHK_EXPECT_STRING:
chunk_appendf(chk, " (expect string '%s')", expect->string);
break;
case TCPCHK_EXPECT_BINARY:
chunk_appendf(chk, " (expect binary '%s')", expect->string);
break;
case TCPCHK_EXPECT_REGEX:
chunk_appendf(chk, " (expect regex)");
break;
case TCPCHK_EXPECT_UNDEF:
chunk_appendf(chk, " (undefined expect!)");
break;
}
}
else if (check->last_started_step && check->last_started_step->action == TCPCHK_ACT_SEND) {
chunk_appendf(chk, " (send)");
@ -3100,6 +3112,8 @@ static int tcpcheck_main(struct check *check)
} /* end 'send' */
else if (check->current_step->action == TCPCHK_ACT_EXPECT) {
struct tcpcheck_expect *expect = &check->current_step->expect;
if (unlikely(check->result == CHK_RES_FAILED))
goto out_end_tcpcheck;
@ -3128,7 +3142,7 @@ static int tcpcheck_main(struct check *check)
}
/* Having received new data, reset the expect chain to its head. */
check->current_step = check->current_step->expect_head;
check->current_step = expect->head;
/* mark the step as started */
check->last_started_step = check->current_step;
@ -3165,39 +3179,61 @@ static int tcpcheck_main(struct check *check)
}
tcpcheck_expect:
/* The current expect might need more data than the previous one, check again
* that the minimum amount data required to match is respected.
*/
if (!done &&
(((check->current_step->string != NULL) && (b_data(&check->bi) < check->current_step->string_len)) ||
((check->current_step->min_recv > 0 && (b_data(&check->bi) < check->current_step->min_recv)))))
continue; /* try to read more */
if (check->current_step->string != NULL)
ret = my_memmem(contentptr, b_data(&check->bi), check->current_step->string, check->current_step->string_len) != NULL;
else if (check->current_step->expect_regex != NULL)
ret = regex_exec(check->current_step->expect_regex, contentptr);
if (!done) {
if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
(b_data(&check->bi) < expect->length))
continue; /* try to read more */
if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv))
continue; /* try to read more */
}
switch (expect->type) {
case TCPCHK_EXPECT_STRING:
case TCPCHK_EXPECT_BINARY:
ret = my_memmem(contentptr, b_data(&check->bi), expect->string, expect->length) != NULL;
break;
case TCPCHK_EXPECT_REGEX:
ret = regex_exec(expect->regex, contentptr);
break;
case TCPCHK_EXPECT_UNDEF:
/* Should never happen. */
retcode = -1;
goto out;
}
/* Wait for more data on mismatch only if no minimum is defined (-1),
* otherwise the absence of match is already conclusive.
*/
if (!ret && !done && (check->current_step->min_recv == -1))
if (!ret && !done && (expect->min_recv == -1))
continue; /* try to read more */
/* matched */
step = tcpcheck_get_step_id(check);
if (ret) {
/* matched but we did not want to => ERROR */
if (check->current_step->inverse) {
/* we were looking for a string */
if (check->current_step->string != NULL) {
if (expect->inverse) {
switch (expect->type) {
case TCPCHK_EXPECT_STRING:
chunk_printf(&trash, "TCPCHK matched unwanted content '%s' at step %d",
check->current_step->string, step);
}
else {
/* we were looking for a regex */
chunk_printf(&trash, "TCPCHK matched unwanted content (regex) at step %d", step);
expect->string, step);
break;
case TCPCHK_EXPECT_BINARY:
chunk_printf(&trash, "TCPCHK matched unwanted content (binary) at step %d",
step);
break;
case TCPCHK_EXPECT_REGEX:
chunk_printf(&trash, "TCPCHK matched unwanted content (regex) at step %d",
step);
break;
case TCPCHK_EXPECT_UNDEF:
/* Should never happen. */
retcode = -1;
goto out;
}
comment = tcpcheck_get_step_comment(check, step);
if (comment)
chunk_appendf(&trash, " comment: '%s'", comment);
@ -3218,14 +3254,16 @@ static int tcpcheck_main(struct check *check)
if (&check->current_step->list == head)
break;
if (check->current_step->action == TCPCHK_ACT_EXPECT)
if (check->current_step->action == TCPCHK_ACT_EXPECT) {
expect = &check->current_step->expect;
goto tcpcheck_expect;
}
}
}
else {
/* not matched */
/* not matched and was not supposed to => OK, next step */
if (check->current_step->inverse) {
if (expect->inverse) {
/* allow next rule */
check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
@ -3237,21 +3275,32 @@ static int tcpcheck_main(struct check *check)
if (&check->current_step->list == head)
break;
if (check->current_step->action == TCPCHK_ACT_EXPECT)
if (check->current_step->action == TCPCHK_ACT_EXPECT) {
expect = &check->current_step->expect;
goto tcpcheck_expect;
}
}
/* not matched but was supposed to => ERROR */
else {
/* we were looking for a string */
if (check->current_step->string != NULL) {
switch (expect->type) {
case TCPCHK_EXPECT_STRING:
chunk_printf(&trash, "TCPCHK did not match content '%s' at step %d",
check->current_step->string, step);
}
else {
/* we were looking for a regex */
break;
case TCPCHK_EXPECT_BINARY:
chunk_printf(&trash, "TCPCHK did not match content (binary) at step %d",
step);
break;
case TCPCHK_EXPECT_REGEX:
chunk_printf(&trash, "TCPCHK did not match content (regex) at step %d",
step);
step);
break;
case TCPCHK_EXPECT_UNDEF:
/* Should never happen. */
retcode = -1;
goto out;
}
comment = tcpcheck_get_step_comment(check, step);
if (comment)
chunk_appendf(&trash, " comment: '%s'", comment);
@ -3358,8 +3407,17 @@ void email_alert_free(struct email_alert *alert)
list_for_each_entry_safe(rule, back, &alert->tcpcheck_rules, list) {
LIST_DEL(&rule->list);
free(rule->comment);
free(rule->string);
regex_free(rule->expect_regex);
switch (rule->expect.type) {
case TCPCHK_EXPECT_STRING:
case TCPCHK_EXPECT_BINARY:
free(rule->expect.string);
break;
case TCPCHK_EXPECT_REGEX:
regex_free(rule->expect.regex);
break;
case TCPCHK_EXPECT_UNDEF:
break;
}
pool_free(pool_head_tcpcheck_rule, rule);
}
pool_free(pool_head_email_alert, alert);
@ -3480,27 +3538,30 @@ int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
static int add_tcpcheck_expect_str(struct list *list, const char *str)
{
struct tcpcheck_rule *tcpcheck, *prev_check;
struct tcpcheck_expect *expect;
if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
return 0;
memset(tcpcheck, 0, sizeof(*tcpcheck));
tcpcheck->action = TCPCHK_ACT_EXPECT;
tcpcheck->string = strdup(str);
tcpcheck->expect_regex = NULL;
tcpcheck->comment = NULL;
if (!tcpcheck->string) {
tcpcheck->action = TCPCHK_ACT_EXPECT;
expect = &tcpcheck->expect;
expect->type = TCPCHK_EXPECT_STRING;
expect->string = strdup(str);
if (!expect->string) {
pool_free(pool_head_tcpcheck_rule, tcpcheck);
return 0;
}
expect->length = strlen(expect->string);
/* All tcp-check expect points back to the first inverse expect rule
* in a chain of one or more expect rule, potentially itself.
*/
tcpcheck->expect_head = tcpcheck;
tcpcheck->expect.head = tcpcheck;
list_for_each_entry_rev(prev_check, list, list) {
if (prev_check->action == TCPCHK_ACT_EXPECT) {
if (prev_check->inverse)
tcpcheck->expect_head = prev_check;
if (prev_check->expect.inverse)
tcpcheck->expect.head = prev_check;
continue;
}
if (prev_check->action != TCPCHK_ACT_COMMENT)
@ -3521,7 +3582,6 @@ static int add_tcpcheck_send_strs(struct list *list, const char * const *strs)
return 0;
memset(tcpcheck, 0, sizeof(*tcpcheck));
tcpcheck->action = TCPCHK_ACT_SEND;
tcpcheck->expect_regex = NULL;
tcpcheck->comment = NULL;
tcpcheck->string_len = 0;
for (i = 0; strs[i]; i++)
@ -3561,7 +3621,6 @@ static int enqueue_one_email_alert(struct proxy *p, struct server *s,
tcpcheck->action = TCPCHK_ACT_CONNECT;
tcpcheck->comment = NULL;
tcpcheck->string = NULL;
tcpcheck->expect_regex = NULL;
LIST_ADDQ(&alert->tcpcheck_rules, &tcpcheck->list);
if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "220 "))