]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: checks: define a tcp expect type
authorGaetan Rivet <grive@u256.net>
Fri, 7 Feb 2020 14:37:17 +0000 (15:37 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 27 Apr 2020 07:39:37 +0000 (09:39 +0200)
Extract the expect definition from its tcpcheck ; create a standalone type.

include/types/checks.h
src/cfgparse-listen.c
src/checks.c

index d284d3f0e145ccca77d2fe8a865d7bb6ac30a40f..36b22fede1fb6ee850c3a88089a676499c24af8f 100644 (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 */
index 1f74ddbe4ea6b6174ff595c60ff7554c652a16d1..6c1d986355c6339fd5a6a7523eca49d87611e8d6 100644 (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)
index 65030afe6dca84c20b49e9e34cfe2ad0cbe47c6c..ef82c4110dd738dd5e85b02dc86cafb1eade9806 100644 (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 "))