mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-05-04 20:46:11 +02:00
MINOR: checks: define a tcp expect type
Extract the expect definition from its tcpcheck ; create a standalone type.
This commit is contained in:
parent
f8ba6773e5
commit
b616add793
@ -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 */
|
||||
|
||||
@ -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)
|
||||
|
||||
145
src/checks.c
145
src/checks.c
@ -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 "))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user