From: Gaetan Rivet Date: Fri, 7 Feb 2020 14:37:17 +0000 (+0100) Subject: MINOR: checks: define a tcp expect type X-Git-Tag: v2.2-dev7~172 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b616add793a3117690020b29e21a846635c142de;p=thirdparty%2Fhaproxy.git MINOR: checks: define a tcp expect type Extract the expect definition from its tcpcheck ; create a standalone type. --- diff --git a/include/types/checks.h b/include/types/checks.h index d284d3f0e1..36b22fede1 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -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 */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 1f74ddbe4e..6c1d986355 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -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 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) diff --git a/src/checks.c b/src/checks.c index 65030afe6d..ef82c4110d 100644 --- a/src/checks.c +++ b/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 "))