mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-05-11 12:18:08 +00:00
MEDIUM: proxy/checks: Register a keyword to parse tcp-check rules
The keyword 'tcp-check' is now parsed in a dedicated callback function. Thus the code to parse these rules is now located in checks.c. In addition, a deinit function have been added to release proxy tcp-check rules, on error or when HAProxy is stopped. This patch is based on Gaetan Rivet work. It uses a callback function registerd on the 'tcp-check' keyword instead, but the spirit is the same.
This commit is contained in:
parent
1d22d7ec0e
commit
fd6c2291bb
@ -162,55 +162,6 @@ int warnif_misplaced_tcp_conn(struct proxy *proxy, const char *file, int line, c
|
||||
warnif_misplaced_tcp_sess(proxy, file, line, arg);
|
||||
}
|
||||
|
||||
/* Parse a comment string for an expect check rule to find a potential
|
||||
* regex backreference. If so, check that it is valid.
|
||||
* returns:
|
||||
* 0 if none found.
|
||||
* 1 if at least one found and all are valid.
|
||||
* -1 if at least one found and at least one is invalid.
|
||||
*/
|
||||
static int find_and_check_backreferences(const char *str, char **err)
|
||||
{
|
||||
static char *errors[] = {
|
||||
"invalid backreference value",
|
||||
"backreference is not within range [1, 9]",
|
||||
};
|
||||
char *backslash;
|
||||
unsigned long int ref;
|
||||
int found = 0;
|
||||
|
||||
while ((backslash = strchr(str, '\\'))) {
|
||||
char *next, *end;
|
||||
|
||||
next = backslash + 1;
|
||||
if (!isdigit(*next)) {
|
||||
str = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
ref = strtoul(next, &end, 10);
|
||||
if (errno == EINVAL) {
|
||||
*err = errors[0];
|
||||
return -1;
|
||||
}
|
||||
else if (errno == ERANGE) {
|
||||
*err = errors[1];
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ref == 0 || ref > 9) {
|
||||
*err = errors[1];
|
||||
return -1;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
str = end;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
{
|
||||
static struct proxy *curproxy = NULL;
|
||||
@ -3084,358 +3035,6 @@ stats_error_parsing:
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(args[0], "tcp-check")) {
|
||||
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
|
||||
if (curproxy == &defproxy) {
|
||||
ha_alert("parsing [%s:%d]: '%s' not allowed in 'defaults' section.\n",
|
||||
file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (curproxy->tcpcheck_rules == NULL) {
|
||||
curproxy->tcpcheck_rules = calloc(1, sizeof(*curproxy->tcpcheck_rules));
|
||||
if (curproxy->tcpcheck_rules == NULL) {
|
||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||
err_code |= ERR_ALERT | ERR_ABORT;
|
||||
goto out;
|
||||
}
|
||||
LIST_INIT(curproxy->tcpcheck_rules);
|
||||
}
|
||||
|
||||
if (strcmp(args[1], "comment") == 0) {
|
||||
int cur_arg;
|
||||
struct tcpcheck_rule *tcpcheck;
|
||||
|
||||
cur_arg = 1;
|
||||
tcpcheck = calloc(1, sizeof(*tcpcheck));
|
||||
tcpcheck->action = TCPCHK_ACT_COMMENT;
|
||||
|
||||
if (!*args[cur_arg + 1]) {
|
||||
ha_alert("parsing [%s:%d] : '%s' expects a comment string.\n",
|
||||
file, linenum, args[cur_arg]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tcpcheck->comment = strdup(args[cur_arg + 1]);
|
||||
|
||||
LIST_ADDQ(curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||
if (alertif_too_many_args_idx(1, 1, file, linenum, args, &err_code))
|
||||
goto out;
|
||||
}
|
||||
else if (strcmp(args[1], "connect") == 0) {
|
||||
const char *ptr_arg;
|
||||
int cur_arg;
|
||||
struct tcpcheck_rule *tcpcheck;
|
||||
|
||||
/* check if first rule is also a 'connect' action */
|
||||
tcpcheck = LIST_NEXT(curproxy->tcpcheck_rules, struct tcpcheck_rule *, list);
|
||||
while (&tcpcheck->list != curproxy->tcpcheck_rules &&
|
||||
tcpcheck->action == TCPCHK_ACT_COMMENT) {
|
||||
tcpcheck = LIST_NEXT(&tcpcheck->list, struct tcpcheck_rule *, list);
|
||||
}
|
||||
|
||||
if (&tcpcheck->list != curproxy->tcpcheck_rules
|
||||
&& tcpcheck->action != TCPCHK_ACT_CONNECT) {
|
||||
ha_alert("parsing [%s:%d] : first step MUST also be a 'connect' when there is a 'connect' step in the tcp-check ruleset.\n",
|
||||
file, linenum);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cur_arg = 2;
|
||||
tcpcheck = calloc(1, sizeof(*tcpcheck));
|
||||
tcpcheck->action = TCPCHK_ACT_CONNECT;
|
||||
|
||||
/* parsing each parameters to fill up the rule */
|
||||
while (*(ptr_arg = args[cur_arg])) {
|
||||
/* tcp port */
|
||||
if (strcmp(args[cur_arg], "port") == 0) {
|
||||
if ( (atol(args[cur_arg + 1]) > 65535) ||
|
||||
(atol(args[cur_arg + 1]) < 1) ){
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects a valid TCP port (from range 1 to 65535), got %s.\n",
|
||||
file, linenum, args[0], args[1], "port", args[cur_arg + 1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
tcpcheck->port = atol(args[cur_arg + 1]);
|
||||
cur_arg += 2;
|
||||
}
|
||||
/* send proxy protocol */
|
||||
else if (strcmp(args[cur_arg], "send-proxy") == 0) {
|
||||
tcpcheck->conn_opts |= TCPCHK_OPT_SEND_PROXY;
|
||||
cur_arg++;
|
||||
}
|
||||
#ifdef USE_OPENSSL
|
||||
else if (strcmp(args[cur_arg], "ssl") == 0) {
|
||||
curproxy->options |= PR_O_TCPCHK_SSL;
|
||||
tcpcheck->conn_opts |= TCPCHK_OPT_SSL;
|
||||
cur_arg++;
|
||||
}
|
||||
#endif /* USE_OPENSSL */
|
||||
else if (strcmp(args[cur_arg], "linger") == 0) {
|
||||
tcpcheck->conn_opts |= TCPCHK_OPT_LINGER;
|
||||
cur_arg++;
|
||||
}
|
||||
/* comment for this tcpcheck line */
|
||||
else if (strcmp(args[cur_arg], "comment") == 0) {
|
||||
if (!*args[cur_arg + 1]) {
|
||||
ha_alert("parsing [%s:%d] : '%s' expects a comment string.\n",
|
||||
file, linenum, args[cur_arg]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
tcpcheck->comment = strdup(args[cur_arg + 1]);
|
||||
cur_arg += 2;
|
||||
}
|
||||
else {
|
||||
#ifdef USE_OPENSSL
|
||||
ha_alert("parsing [%s:%d] : '%s %s' expects 'comment', 'port', 'send-proxy', 'ssl' or 'linger' but got '%s' as argument.\n",
|
||||
#else /* USE_OPENSSL */
|
||||
ha_alert("parsing [%s:%d] : '%s %s' expects 'comment', 'port', 'send-proxy' or 'linger' but got '%s' as argument.\n",
|
||||
#endif /* USE_OPENSSL */
|
||||
file, linenum, args[0], args[1], args[cur_arg]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LIST_ADDQ(curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||
}
|
||||
else if (strcmp(args[1], "send") == 0) {
|
||||
if (! *(args[2]) ) {
|
||||
/* SEND string expected */
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects <STRING> as argument.\n",
|
||||
file, linenum, args[0], args[1], args[2]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
} else {
|
||||
struct tcpcheck_rule *tcpcheck;
|
||||
|
||||
tcpcheck = calloc(1, sizeof(*tcpcheck));
|
||||
|
||||
tcpcheck->action = TCPCHK_ACT_SEND;
|
||||
tcpcheck->string_len = strlen(args[2]);
|
||||
tcpcheck->string = strdup(args[2]);
|
||||
|
||||
/* comment for this tcpcheck line */
|
||||
if (strcmp(args[3], "comment") == 0) {
|
||||
if (!*args[4]) {
|
||||
ha_alert("parsing [%s:%d] : '%s' expects a comment string.\n",
|
||||
file, linenum, args[3]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
tcpcheck->comment = strdup(args[4]);
|
||||
}
|
||||
|
||||
LIST_ADDQ(curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[1], "send-binary") == 0) {
|
||||
if (! *(args[2]) ) {
|
||||
/* SEND binary string expected */
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects <BINARY STRING> as argument.\n",
|
||||
file, linenum, args[0], args[1], args[2]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
} else {
|
||||
struct tcpcheck_rule *tcpcheck;
|
||||
char *err = NULL;
|
||||
|
||||
tcpcheck = calloc(1, sizeof(*tcpcheck));
|
||||
|
||||
tcpcheck->action = TCPCHK_ACT_SEND;
|
||||
if (parse_binary(args[2], &tcpcheck->string, &tcpcheck->string_len, &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;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* comment for this tcpcheck line */
|
||||
if (strcmp(args[3], "comment") == 0) {
|
||||
if (!*args[4]) {
|
||||
ha_alert("parsing [%s:%d] : '%s' expects a comment string.\n",
|
||||
file, linenum, args[3]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
tcpcheck->comment = strdup(args[4]);
|
||||
}
|
||||
|
||||
LIST_ADDQ(curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||
}
|
||||
}
|
||||
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;
|
||||
int inverse = 0;
|
||||
|
||||
if (curproxy->options2 & PR_O2_EXP_TYPE) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s' already specified.\n", file, linenum, args[0], args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cur_arg = 2;
|
||||
|
||||
/* Parse potential the minimum amount of data
|
||||
* required before proceeding with the match.
|
||||
*/
|
||||
if (strcmp(args[cur_arg], "min-recv") == 0) {
|
||||
if (!*(args[cur_arg + 1])) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects an integer as an argument.\n",
|
||||
file, linenum, args[0], args[1], args[2]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Use an signed integer here because of chksize */
|
||||
min_recv = atol(args[cur_arg + 1]);
|
||||
if (min_recv < -1 || min_recv > INT_MAX) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects -1 or an integer from 0 to INT_MAX.\n",
|
||||
file, linenum, args[0], args[1], args[2]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cur_arg += 2;
|
||||
}
|
||||
|
||||
/* consider exclamation marks, sole or at the beginning of a word */
|
||||
while (*(ptr_arg = args[cur_arg])) {
|
||||
while (*ptr_arg == '!') {
|
||||
inverse = !inverse;
|
||||
ptr_arg++;
|
||||
}
|
||||
if (*ptr_arg)
|
||||
break;
|
||||
cur_arg++;
|
||||
}
|
||||
|
||||
/* now ptr_arg points to the beginning of a word past any possible
|
||||
* exclamation mark, and cur_arg is the argument which holds this word.
|
||||
*/
|
||||
|
||||
tcpcheck = calloc(1, sizeof(*tcpcheck));
|
||||
tcpcheck->action = TCPCHK_ACT_EXPECT;
|
||||
expect = &tcpcheck->expect;
|
||||
expect->inverse = inverse;
|
||||
expect->min_recv = min_recv;
|
||||
|
||||
if (strcmp(ptr_arg, "binary") == 0) {
|
||||
char *err = NULL;
|
||||
|
||||
if (!*(args[cur_arg + 1])) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects <binary string> as an argument.\n",
|
||||
file, linenum, args[0], args[1], ptr_arg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (strcmp(ptr_arg, "string") == 0) {
|
||||
if (!*(args[cur_arg + 1])) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects <string> as an argument.\n",
|
||||
file, linenum, args[0], args[1], ptr_arg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
expect->type = TCPCHK_EXPECT_STRING;
|
||||
expect->string = strdup(args[cur_arg + 1]);
|
||||
expect->length = strlen(expect->string);
|
||||
}
|
||||
else if (strcmp(ptr_arg, "rstring") == 0 ||
|
||||
strcmp(ptr_arg, "rbinary") == 0) {
|
||||
if (!*(args[cur_arg + 1])) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects <regex> as an argument.\n",
|
||||
file, linenum, args[0], args[1], ptr_arg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
expect->type = ((strcmp(ptr_arg, "rbinary") == 0) ? TCPCHK_EXPECT_REGEX_BINARY : TCPCHK_EXPECT_REGEX);
|
||||
error = NULL;
|
||||
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);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ha_alert("parsing [%s:%d] : '%s %s' only supports [!] 'binary', 'string', 'rstring', 'rbinary', found '%s'.\n",
|
||||
file, linenum, args[0], args[1], ptr_arg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* tcpcheck comment */
|
||||
cur_arg += 2;
|
||||
if (strcmp(args[cur_arg], "comment") == 0) {
|
||||
if (!*args[cur_arg + 1]) {
|
||||
ha_alert("parsing [%s:%d] : '%s' expects a comment string.\n",
|
||||
file, linenum, args[cur_arg + 1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
tcpcheck->comment = strdup(args[cur_arg + 1]);
|
||||
rc = find_and_check_backreferences(tcpcheck->comment, &error);
|
||||
if (rc > 0) {
|
||||
if (!inverse) {
|
||||
ha_warning("parsing [%s:%d] : "
|
||||
"using backreference in a positive expect comment is useless.\n",
|
||||
file, linenum);
|
||||
err_code |= ERR_WARN;
|
||||
}
|
||||
expect->with_capture = 1;
|
||||
}
|
||||
else if (rc < 0) {
|
||||
ha_alert("parsing [%s:%d] : %s.\n",
|
||||
file, linenum, error);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
list_for_each_entry_rev(prev_check, curproxy->tcpcheck_rules, list) {
|
||||
if (prev_check->action == TCPCHK_ACT_EXPECT) {
|
||||
if (prev_check->expect.inverse)
|
||||
tcpcheck->expect.head = prev_check;
|
||||
continue;
|
||||
}
|
||||
if (prev_check->action != TCPCHK_ACT_COMMENT)
|
||||
break;
|
||||
}
|
||||
LIST_ADDQ(curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||
}
|
||||
else {
|
||||
ha_alert("parsing [%s:%d] : '%s' only supports 'comment', 'connect', 'send' or 'expect'.\n", file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(args[0], "monitor")) {
|
||||
if (curproxy == &defproxy) {
|
||||
ha_alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
|
460
src/checks.c
460
src/checks.c
@ -30,6 +30,7 @@
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <common/cfgparse.h>
|
||||
#include <common/chunk.h>
|
||||
#include <common/compat.h>
|
||||
#include <common/config.h>
|
||||
@ -3366,6 +3367,31 @@ void free_check(struct check *check)
|
||||
}
|
||||
}
|
||||
|
||||
static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
|
||||
{
|
||||
if (!rule)
|
||||
return;
|
||||
|
||||
free(rule->comment);
|
||||
free(rule->string);
|
||||
switch (rule->expect.type) {
|
||||
case TCPCHK_EXPECT_STRING:
|
||||
case TCPCHK_EXPECT_BINARY:
|
||||
free(rule->expect.string);
|
||||
break;
|
||||
case TCPCHK_EXPECT_REGEX:
|
||||
case TCPCHK_EXPECT_REGEX_BINARY:
|
||||
regex_free(rule->expect.regex);
|
||||
break;
|
||||
case TCPCHK_EXPECT_UNDEF:
|
||||
break;
|
||||
}
|
||||
if (in_pool)
|
||||
pool_free(pool_head_tcpcheck_rule, rule);
|
||||
else
|
||||
free(rule);
|
||||
}
|
||||
|
||||
void email_alert_free(struct email_alert *alert)
|
||||
{
|
||||
struct tcpcheck_rule *rule, *back;
|
||||
@ -3375,20 +3401,7 @@ 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);
|
||||
switch (rule->expect.type) {
|
||||
case TCPCHK_EXPECT_STRING:
|
||||
case TCPCHK_EXPECT_BINARY:
|
||||
free(rule->expect.string);
|
||||
break;
|
||||
case TCPCHK_EXPECT_REGEX:
|
||||
case TCPCHK_EXPECT_REGEX_BINARY:
|
||||
regex_free(rule->expect.regex);
|
||||
break;
|
||||
case TCPCHK_EXPECT_UNDEF:
|
||||
break;
|
||||
}
|
||||
pool_free(pool_head_tcpcheck_rule, rule);
|
||||
free_tcpcheck(rule, 1);
|
||||
}
|
||||
pool_free(pool_head_email_alert, alert);
|
||||
}
|
||||
@ -3863,6 +3876,21 @@ static int init_srv_agent_check(struct server *srv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void deinit_proxy_tcpcheck(struct proxy *px)
|
||||
{
|
||||
struct tcpcheck_rule *chk, *back;
|
||||
|
||||
if (!px->tcpcheck_rules)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(chk, back, px->tcpcheck_rules, list) {
|
||||
LIST_DEL(&chk->list);
|
||||
free_tcpcheck(chk, 0);
|
||||
}
|
||||
free(px->tcpcheck_rules);
|
||||
px->tcpcheck_rules = NULL;
|
||||
}
|
||||
|
||||
static void deinit_srv_check(struct server *srv)
|
||||
{
|
||||
if (srv->do_check)
|
||||
@ -3880,9 +3908,413 @@ static void deinit_srv_agent_check(struct server *srv)
|
||||
REGISTER_POST_SERVER_CHECK(init_srv_check);
|
||||
REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
|
||||
|
||||
REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
|
||||
REGISTER_SERVER_DEINIT(deinit_srv_check);
|
||||
REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
|
||||
|
||||
static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
|
||||
char **errmsg)
|
||||
{
|
||||
struct tcpcheck_rule *chk = NULL;
|
||||
char *comment = NULL;
|
||||
unsigned short conn_opts = 0;
|
||||
long port = 0;
|
||||
|
||||
list_for_each_entry(chk, rules, list) {
|
||||
if (chk->action != TCPCHK_ACT_COMMENT)
|
||||
break;
|
||||
}
|
||||
if (&chk->list != rules && chk->action != TCPCHK_ACT_CONNECT) {
|
||||
memprintf(errmsg, "first step MUST also be a 'connect' when there is a 'connect' step in the tcp-check ruleset");
|
||||
goto error;
|
||||
}
|
||||
|
||||
cur_arg++;
|
||||
while (*(args[cur_arg])) {
|
||||
if (strcmp(args[cur_arg], "port") == 0) {
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "'%s' expects a port number as argument.", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
cur_arg++;
|
||||
port = atol(args[cur_arg]);
|
||||
if (port > 65535 || port < 1) {
|
||||
memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535), got %s.", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[cur_arg], "comment") == 0) {
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
cur_arg++;
|
||||
free(comment);
|
||||
comment = strdup(args[cur_arg]);
|
||||
if (!comment) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[cur_arg], "send-proxy") == 0)
|
||||
conn_opts |= TCPCHK_OPT_SEND_PROXY;
|
||||
else if (strcmp(args[cur_arg], "linger") == 0)
|
||||
conn_opts |= TCPCHK_OPT_LINGER;
|
||||
#ifdef USE_OPENSSL
|
||||
else if (strcmp(args[cur_arg], "ssl") == 0) {
|
||||
px->options |= PR_O_TCPCHK_SSL;
|
||||
conn_opts |= TCPCHK_OPT_SSL;
|
||||
}
|
||||
#endif /* USE_OPENSSL */
|
||||
|
||||
else {
|
||||
memprintf(errmsg, "expects 'comment', 'port', 'send-proxy'"
|
||||
#ifdef USE_OPENSSL
|
||||
", 'ssl'"
|
||||
#endif /* USE_OPENSSL */
|
||||
" or 'linger' but got '%s' as argument.",
|
||||
args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
cur_arg++;
|
||||
}
|
||||
|
||||
chk = calloc(1, sizeof(*chk));
|
||||
if (!chk) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
chk->action = TCPCHK_ACT_CONNECT;
|
||||
chk->port = port;
|
||||
chk->conn_opts = conn_opts;
|
||||
chk->comment = comment;
|
||||
return chk;
|
||||
|
||||
error:
|
||||
free(comment);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct list *rules, char **errmsg)
|
||||
{
|
||||
struct tcpcheck_rule *chk = NULL;
|
||||
char *str = NULL, *comment = NULL;
|
||||
int len, is_binary;
|
||||
|
||||
is_binary = (strcmp(args[cur_arg], "send-binary") == 0);
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "'%s' expects a %s as argument", (is_binary ? "binary string": "string"), args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (is_binary) {
|
||||
if (parse_binary(args[cur_arg+1], &str, &len, errmsg) == 0) {
|
||||
memprintf(errmsg, "'%s' invalid binary string (%s).\n", args[cur_arg], *errmsg);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
str = strdup(args[cur_arg+1]);
|
||||
len = strlen(args[cur_arg+1]);
|
||||
if (!str) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
cur_arg++;
|
||||
|
||||
if (strcmp(args[cur_arg], "comment") == 0) {
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
cur_arg++;
|
||||
comment = strdup(args[cur_arg]);
|
||||
if (!comment) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
chk = calloc(1, sizeof(*chk));
|
||||
if (!chk) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
chk->action = TCPCHK_ACT_SEND;
|
||||
chk->string = str;
|
||||
chk->string_len = len;
|
||||
chk->comment = comment;
|
||||
return chk;
|
||||
|
||||
error:
|
||||
free(str);
|
||||
free(comment);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct list *rules, char **errmsg)
|
||||
{
|
||||
struct tcpcheck_rule *chk = NULL;
|
||||
char *comment = NULL;
|
||||
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "expects a string as argument");
|
||||
goto error;
|
||||
}
|
||||
cur_arg++;
|
||||
comment = strdup(args[cur_arg]);
|
||||
if (!comment) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
|
||||
chk = calloc(1, sizeof(*chk));
|
||||
if (!chk) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
chk->action = TCPCHK_ACT_COMMENT;
|
||||
chk->comment = comment;
|
||||
return chk;
|
||||
|
||||
error:
|
||||
free(comment);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct list *rules, char **errmsg)
|
||||
{
|
||||
struct tcpcheck_rule *prev_check, *chk = NULL;
|
||||
char *str = NULL, *comment = NULL, *pattern = NULL;
|
||||
enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
|
||||
long min_recv = -1;
|
||||
int inverse = 0, with_capture = 0;
|
||||
|
||||
if (!*(args[cur_arg+1]) || !*(args[cur_arg+2])) {
|
||||
memprintf(errmsg, "expects a pattern (type+string) as arguments");
|
||||
goto error;
|
||||
}
|
||||
|
||||
cur_arg++;
|
||||
while (*(args[cur_arg])) {
|
||||
int in_pattern = 0;
|
||||
|
||||
rescan:
|
||||
if (strcmp(args[cur_arg], "min-recv") == 0) {
|
||||
if (in_pattern) {
|
||||
memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
/* Use an signed integer here because of chksize */
|
||||
cur_arg++;
|
||||
min_recv = atol(args[cur_arg]);
|
||||
if (min_recv < -1 || min_recv > INT_MAX) {
|
||||
memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (*(args[cur_arg]) == '!') {
|
||||
in_pattern = 1;
|
||||
while (*(args[cur_arg]) == '!') {
|
||||
inverse = !inverse;
|
||||
args[cur_arg]++;
|
||||
}
|
||||
if (!*(args[cur_arg]))
|
||||
cur_arg++;
|
||||
goto rescan;
|
||||
}
|
||||
else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "binary") == 0 ||
|
||||
strcmp(args[cur_arg], "rstring") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
|
||||
if (type != TCPCHK_EXPECT_UNDEF) {
|
||||
memprintf(errmsg, "only on pattern expected");
|
||||
goto error;
|
||||
}
|
||||
type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING :
|
||||
((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY :
|
||||
((*(args[cur_arg]+1) == 's') ? TCPCHK_EXPECT_REGEX : TCPCHK_EXPECT_REGEX_BINARY)));
|
||||
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
cur_arg++;
|
||||
pattern = args[cur_arg];
|
||||
}
|
||||
else if (strcmp(args[cur_arg], "comment") == 0) {
|
||||
if (in_pattern) {
|
||||
memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
if (!*(args[cur_arg+1])) {
|
||||
memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
cur_arg++;
|
||||
free(comment);
|
||||
comment = strdup(args[cur_arg]);
|
||||
if (!comment) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]rbinary'"
|
||||
" or comment but got '%s' as argument.", args[cur_arg]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
cur_arg++;
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
char *p = comment;
|
||||
|
||||
while (*p) {
|
||||
if (*p == '\\') {
|
||||
p++;
|
||||
if (!*p || !isdigit((unsigned char)*p) ||
|
||||
(*p == 'x' && (!*(p+1) || !*(p+2) || !ishex(*(p+1)) || !ishex(*(p+2))))) {
|
||||
memprintf(errmsg, "invalid backreference in 'comment' argument");
|
||||
goto error;
|
||||
}
|
||||
with_capture = 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (with_capture && !inverse)
|
||||
memprintf(errmsg, "using backreference in a positive expect comment is useless");
|
||||
}
|
||||
|
||||
chk = calloc(1, sizeof(*chk));
|
||||
if (!chk) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
chk->action = TCPCHK_ACT_EXPECT;
|
||||
chk->comment = comment;
|
||||
chk->expect.type = type;
|
||||
chk->expect.min_recv = min_recv;
|
||||
chk->expect.inverse = inverse;
|
||||
chk->expect.with_capture = with_capture;
|
||||
|
||||
switch (chk->expect.type) {
|
||||
case TCPCHK_EXPECT_STRING:
|
||||
chk->expect.string = strdup(pattern);
|
||||
chk->expect.length = strlen(pattern);
|
||||
if (!chk->expect.string) {
|
||||
memprintf(errmsg, "out of memory");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case TCPCHK_EXPECT_BINARY:
|
||||
if (parse_binary(pattern, &chk->expect.string, &chk->expect.length, errmsg) == 0) {
|
||||
memprintf(errmsg, "invalid binary string (%s)", *errmsg);
|
||||
goto error;
|
||||
}
|
||||
case TCPCHK_EXPECT_REGEX:
|
||||
case TCPCHK_EXPECT_REGEX_BINARY:
|
||||
chk->expect.regex = regex_comp(pattern, 1, with_capture, errmsg);
|
||||
if (!chk->expect.regex)
|
||||
goto error;
|
||||
break;
|
||||
case TCPCHK_EXPECT_UNDEF:
|
||||
free(chk);
|
||||
memprintf(errmsg, "pattern not found");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* All tcp-check expect points back to the first inverse expect rule in
|
||||
* a chain of one or more expect rule, potentially itself.
|
||||
*/
|
||||
chk->expect.head = chk;
|
||||
list_for_each_entry_rev(prev_check, rules, list) {
|
||||
if (prev_check->action == TCPCHK_ACT_EXPECT) {
|
||||
if (prev_check->expect.inverse)
|
||||
chk->expect.head = prev_check;
|
||||
continue;
|
||||
}
|
||||
if (prev_check->action != TCPCHK_ACT_COMMENT)
|
||||
break;
|
||||
}
|
||||
return chk;
|
||||
|
||||
error:
|
||||
free(chk);
|
||||
free(str);
|
||||
free(comment);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Parses the "tcp-check" proxy keyword */
|
||||
static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
|
||||
struct proxy *defpx, const char *file, int line,
|
||||
char **errmsg)
|
||||
{
|
||||
struct list *rules = curpx->tcpcheck_rules;
|
||||
struct tcpcheck_rule *chk = NULL;
|
||||
int cur_arg, ret = 0;
|
||||
|
||||
if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
|
||||
ret = 1;
|
||||
|
||||
if (curpx == defpx) {
|
||||
memprintf(errmsg, "'%s' not allowed in 'defaults' section.", args[0]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!rules) {
|
||||
rules = calloc(1, sizeof(*rules));
|
||||
if (!rules) {
|
||||
memprintf(errmsg, "%s : out of memory.", args[0]);
|
||||
goto error;
|
||||
}
|
||||
LIST_INIT(rules);
|
||||
curpx->tcpcheck_rules = rules;
|
||||
}
|
||||
|
||||
cur_arg = 1;
|
||||
if (strcmp(args[cur_arg], "connect") == 0)
|
||||
chk = parse_tcpcheck_connect(args, cur_arg, curpx, rules, errmsg);
|
||||
else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0)
|
||||
chk = parse_tcpcheck_send(args, cur_arg, rules, errmsg);
|
||||
else if (strcmp(args[cur_arg], "expect") == 0)
|
||||
chk = parse_tcpcheck_expect(args, cur_arg, rules, errmsg);
|
||||
else if (strcmp(args[cur_arg], "comment") == 0)
|
||||
chk = parse_tcpcheck_comment(args, cur_arg, rules, errmsg);
|
||||
else {
|
||||
memprintf(errmsg, "'%s %s' only supports 'comment', 'connect', 'send', 'send-binary' or 'expect'.",
|
||||
args[0], args[1]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!chk) {
|
||||
memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
|
||||
goto error;
|
||||
}
|
||||
ret = (*errmsg != NULL); /* Handle warning */
|
||||
|
||||
/* No error: add the tcp-check rule in the list */
|
||||
LIST_ADDQ(rules, &chk->list);
|
||||
return ret;
|
||||
|
||||
error:
|
||||
if (rules)
|
||||
deinit_proxy_tcpcheck(curpx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct cfg_kw_list cfg_kws = {ILH, {
|
||||
{ CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
|
||||
{ 0, NULL, NULL },
|
||||
}};
|
||||
|
||||
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
|
Loading…
Reference in New Issue
Block a user