diff --git a/include/common/defaults.h b/include/common/defaults.h index d52e00580..b63870e65 100644 --- a/include/common/defaults.h +++ b/include/common/defaults.h @@ -177,7 +177,6 @@ #define DEF_AGENT_RISETIME 1 #define DEF_CHECK_REQ "OPTIONS / HTTP/1.0\r\n" #define DEF_CHECK_PATH "" -#define DEF_SMTP_CHECK_REQ "HELO localhost\r\n" #define DEF_LDAP_CHECK_REQ "\x30\x0c\x02\x01\x01\x60\x07\x02\x01\x03\x04\x00\x80\x00" diff --git a/include/proto/checks.h b/include/proto/checks.h index 33ed492c4..b3ea9175b 100644 --- a/include/proto/checks.h +++ b/include/proto/checks.h @@ -3,7 +3,7 @@ Functions prototypes for the checks. Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 2.1 @@ -73,6 +73,8 @@ int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, s const char *file, int line); int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, const char *file, int line); +int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line); #endif /* _PROTO_CHECKS_H */ diff --git a/include/types/checks.h b/include/types/checks.h index 4830f0d94..541666c66 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -313,6 +313,7 @@ struct tcpcheck_rule { #define TCPCHK_RULES_DEF 0x00000002 /* Ruleset inherited from the default section */ #define TCPCHK_RULES_REDIS_CHK 0x00000020 +#define TCPCHK_RULES_SMTP_CHK 0x00000030 #define TCPCHK_RULES_SSL3_CHK 0x00000070 /* A list of tcp-check vars, to be registered before executing a ruleset */ diff --git a/include/types/proxy.h b/include/types/proxy.h index 9ace915d9..0f4301d27 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -171,7 +171,7 @@ enum PR_SRV_STATE_FILE { #define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */ #define PR_O2_PGSQL_CHK 0x10000000 /* use PGSQL check for server health */ /* unused: 0x20000000 */ -#define PR_O2_SMTP_CHK 0x30000000 /* use SMTP EHLO check for server health - pvandijk@vision6.com.au */ +/* unused 0x30000000 */ #define PR_O2_HTTP_CHK 0x40000000 /* use HTTP 'OPTIONS' method to check server health */ #define PR_O2_MYSQL_CHK 0x50000000 /* use MYSQL check for server health */ #define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 291dca805..e85cde2d5 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -2402,29 +2402,8 @@ stats_error_parsing: goto out; } else if (!strcmp(args[1], "smtpchk")) { - /* use SMTP request to check servers' health */ - free(curproxy->check_req); - curproxy->check_req = NULL; - curproxy->options2 &= ~PR_O2_CHK_ANY; - curproxy->options2 |= PR_O2_SMTP_CHK; - - if (!*args[2] || !*args[3]) { /* no argument or incomplete EHLO host */ - curproxy->check_req = strdup(DEF_SMTP_CHECK_REQ); /* default request */ - curproxy->check_len = strlen(DEF_SMTP_CHECK_REQ); - } else { /* ESMTP EHLO, or SMTP HELO, and a hostname */ - if (!strcmp(args[2], "EHLO") || !strcmp(args[2], "HELO")) { - int reqlen = strlen(args[2]) + strlen(args[3]) + strlen(" \r\n") + 1; - curproxy->check_req = malloc(reqlen); - curproxy->check_len = snprintf(curproxy->check_req, reqlen, - "%s %s\r\n", args[2], args[3]); /* HELO hostname */ - } else { - /* this just hits the default for now, but you could potentially expand it to allow for other stuff - though, it's unlikely you'd want to send anything other than an EHLO or HELO */ - curproxy->check_req = strdup(DEF_SMTP_CHECK_REQ); /* default request */ - curproxy->check_len = strlen(DEF_SMTP_CHECK_REQ); - } - } - if (alertif_too_many_args_idx(2, 1, file, linenum, args, &err_code)) + err_code |= proxy_parse_smtpchk_opt(args, 0, curproxy, &defproxy, file, linenum); + if (err_code & ERR_FATAL) goto out; } else if (!strcmp(args[1], "pgsql-check")) { diff --git a/src/checks.c b/src/checks.c index 0c62a03b0..b0e770480 100644 --- a/src/checks.c +++ b/src/checks.c @@ -949,36 +949,6 @@ static void __event_srv_chk_r(struct conn_stream *cs) } break; - case PR_O2_SMTP_CHK: - if (!done && b_data(&check->bi) < strlen("000\r")) - goto wait_more_data; - - /* do not reset when closing, servers don't like this */ - if (conn_ctrl_ready(cs->conn)) - fdtab[cs->conn->handle.fd].linger_risk = 0; - - /* Check if the server speaks SMTP */ - if ((b_data(&check->bi) < strlen("000\r")) || - (*(b_head(&check->bi) + 3) != ' ' && *(b_head(&check->bi) + 3) != '\r') || - !isdigit((unsigned char) *b_head(&check->bi)) || !isdigit((unsigned char) *(b_head(&check->bi) + 1)) || - !isdigit((unsigned char) *(b_head(&check->bi) + 2))) { - cut_crlf(b_head(&check->bi)); - set_server_check_status(check, HCHK_STATUS_L7RSP, b_head(&check->bi)); - goto out_wakeup; - } - - check->code = str2uic(b_head(&check->bi)); - - desc = ltrim(b_head(&check->bi) + 3, ' '); - cut_crlf(desc); - - /* Check for SMTP code 2xx (should be 250) */ - if (*b_head(&check->bi) == '2') - set_server_check_status(check, HCHK_STATUS_L7OKD, desc); - else - set_server_check_status(check, HCHK_STATUS_L7STS, desc); - break; - case PR_O2_LB_AGENT_CHK: { int status = HCHK_STATUS_CHECKED; const char *hs = NULL; /* health status */ @@ -5282,6 +5252,153 @@ int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, goto out; } +/* Parses the "option smtpchk" proxy keyword */ +int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line) +{ + static char *smtp_req = "%[var(check.smtp_cmd)]\r\n"; + + struct tcpcheck_ruleset *rs = NULL; + struct tcpcheck_rules *rules = &curpx->tcpcheck_rules; + struct tcpcheck_rule *chk; + struct tcpcheck_var *var = NULL; + char *cmd = NULL, *errmsg = NULL; + int err_code = 0; + + if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL)) + err_code |= ERR_WARN; + + if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code)) + goto out; + + if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) { + ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n", + file, line); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + curpx->options2 &= ~PR_O2_CHK_ANY; + curpx->options2 |= PR_O2_TCPCHK_CHK; + + free_tcpcheck_vars(&rules->preset_vars); + rules->list = NULL; + rules->flags = 0; + + cur_arg += 2; + if (*args[cur_arg] && *args[cur_arg+1] && + (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) { + cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd)); + if (cmd) + sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]); + } + else { + /* this just hits the default for now, but you could potentially expand it to allow for other stuff + though, it's unlikely you'd want to send anything other than an EHLO or HELO */ + cmd = strdup("HELO localhost"); + } + + var = tcpcheck_var_create("check.smtp_cmd"); + if (cmd == NULL || var == NULL) { + ha_alert("parsing [%s:%d] : out of memory.\n", file, line); + goto error; + } + var->data.type = SMP_T_STR; + var->data.u.str.area = cmd; + var->data.u.str.data = strlen(cmd); + LIST_INIT(&var->list); + LIST_ADDQ(&rules->preset_vars, &var->list); + cmd = NULL; + var = NULL; + + rs = tcpcheck_ruleset_lookup("*smtp-check"); + if (rs) + goto ruleset_found; + + rs = tcpcheck_ruleset_create("*smtp-check"); + if (rs == NULL) { + ha_alert("parsing [%s:%d] : out of memory.\n", file, line); + goto error; + } + + chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 0; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]", + "min-recv", "4", + "error-status", "L7RSP", + "on-error", "%[check.payload(),cut_crlf]", + ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 1; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]", + "min-recv", "4", + "error-status", "L7STS", + "on-error", "%[check.payload(4,0),ltrim(' '),cut_crlf]", + "status-code", "check.payload(0,3)", + ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 2; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", smtp_req, "log-format", ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 3; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]", + "min-recv", "4", + "error-status", "L7STS", + "on-error", "%[check.payload(4,0),ltrim(' '),cut_crlf]", + "on-success", "%[check.payload(4,0),ltrim(' '),cut_crlf]", + "status-code", "check.payload(0,3)", + ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 4; + LIST_ADDQ(&rs->rules, &chk->list); + + LIST_ADDQ(&tcpchecks_list, &rs->list); + + ruleset_found: + rules->list = &rs->rules; + rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_SMTP_CHK); + + out: + free(errmsg); + return err_code; + + error: + free(cmd); + free(var); + free_tcpcheck_vars(&rules->preset_vars); + tcpcheck_ruleset_release(rs); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; +} static struct cfg_kw_list cfg_kws = {ILH, { { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },