blob: d5306defe05f77039562ba30fd1c1fcfec4b8f62 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Willy Tarreau9b39dc52014-07-08 00:54:10 +020018#include <signal.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090019#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020020#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020021#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020023#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020024#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010025#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040027#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090028#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020030#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031#include <arpa/inet.h>
32
Christopher Fauletfd6c2292020-03-25 18:20:15 +010033#include <common/cfgparse.h>
Willy Tarreauc7e42382012-08-24 19:22:53 +020034#include <common/chunk.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020035#include <common/compat.h>
36#include <common/config.h>
37#include <common/mini-clist.h>
Willy Tarreau83749182007-04-15 20:56:27 +020038#include <common/standard.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020039#include <common/time.h>
Christopher Fauletcfda8472017-10-20 15:40:23 +020040#include <common/hathreads.h>
Christopher Faulete5870d82020-04-15 11:32:03 +020041#include <common/http.h>
42#include <common/h1.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020043#include <common/htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020044
45#include <types/global.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020046#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010047#include <types/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020048
Gaetan Rivet707b52f2020-02-21 18:14:59 +010049#include <proto/action.h>
Christopher Fauletba3c68f2020-04-01 16:27:05 +020050#include <proto/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020051#include <proto/backend.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020052#include <proto/checks.h>
William Lallemand9ed62032016-11-21 17:49:11 +010053#include <proto/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020054#include <proto/fd.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020055#include <proto/http_htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020056#include <proto/log.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020057#include <proto/mux_pt.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020058#include <proto/queue.h>
Willy Tarreauc6f4ce82009-06-10 11:09:37 +020059#include <proto/port_range.h>
Willy Tarreaue8c66af2008-01-13 18:40:14 +010060#include <proto/proto_tcp.h>
Baptiste Assmann69e273f2013-12-11 00:52:19 +010061#include <proto/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010062#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020063#include <proto/server.h>
Willy Tarreau48d6bf22016-06-21 16:27:34 +020064#include <proto/signal.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010065#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020066#include <proto/task.h>
Gaetan Rivet13a50432020-02-21 18:13:44 +010067#include <proto/vars.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020068#include <proto/log.h>
69#include <proto/dns.h>
70#include <proto/proto_udp.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020071#include <proto/ssl_sock.h>
Christopher Fauletb7d30092020-03-30 15:19:03 +020072#include <proto/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020073
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020074static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010075
Christopher Faulet61cc8522020-04-20 14:54:42 +020076static int wake_srv_chk(struct conn_stream *cs);
77struct data_cb check_conn_cb = {
78 .wake = wake_srv_chk,
79 .name = "CHCK",
80};
Christopher Fauletd7e63962020-04-17 20:15:59 +020081
Christopher Fauletd7cee712020-04-21 13:45:00 +020082/* Global tree to share all tcp-checks */
83struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020084
85
Willy Tarreau8ceae722018-11-26 11:58:30 +010086DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
87DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020088
Gaetan Rivet05d692d2020-02-14 17:42:54 +010089/* Dummy frontend used to create all checks sessions. */
90static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020091
Christopher Faulet61cc8522020-04-20 14:54:42 +020092/**************************************************************************/
93/************************ Handle check results ****************************/
94/**************************************************************************/
95struct check_status {
96 short result; /* one of SRV_CHK_* */
97 char *info; /* human readable short info */
98 char *desc; /* long description */
99};
100
101struct analyze_status {
102 char *desc; /* description */
103 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
104};
105
Simon Horman63a4a822012-03-19 07:24:41 +0900106static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100107 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
108 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200109 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200110
Willy Tarreau23964182014-05-20 20:56:30 +0200111 /* Below we have finished checks */
112 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100114
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100115 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100117 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
118 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
119 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200120
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100121 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
122 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
123 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200124
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100125 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
126 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200127
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200128 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200129
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100130 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
131 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
132 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900133
134 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
135 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200136 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200137};
138
Simon Horman63a4a822012-03-19 07:24:41 +0900139static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100140 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
141
142 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
143 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
144
145 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
146 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
147 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
148 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
149
150 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
151 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
152 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
153};
154
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100155/* checks if <err> is a real error for errno or one that can be ignored, and
156 * return 0 for these ones or <err> for real ones.
157 */
158static inline int unclean_errno(int err)
159{
160 if (err == EAGAIN || err == EINPROGRESS ||
161 err == EISCONN || err == EALREADY)
162 return 0;
163 return err;
164}
165
Christopher Faulet61cc8522020-04-20 14:54:42 +0200166/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200167const char *get_check_status_description(short check_status) {
168
169 const char *desc;
170
171 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200172 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200173 else
174 desc = NULL;
175
176 if (desc && *desc)
177 return desc;
178 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200179 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200180}
181
Christopher Faulet61cc8522020-04-20 14:54:42 +0200182/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200183const char *get_check_status_info(short check_status) {
184
185 const char *info;
186
187 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200188 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200189 else
190 info = NULL;
191
192 if (info && *info)
193 return info;
194 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200195 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200196}
197
Christopher Faulet61cc8522020-04-20 14:54:42 +0200198/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100199const char *get_analyze_status(short analyze_status) {
200
201 const char *desc;
202
203 if (analyze_status < HANA_STATUS_SIZE)
204 desc = analyze_statuses[analyze_status].desc;
205 else
206 desc = NULL;
207
208 if (desc && *desc)
209 return desc;
210 else
211 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
212}
213
Christopher Faulet61cc8522020-04-20 14:54:42 +0200214/* Sets check->status, update check->duration and fill check->result with an
215 * adequate CHK_RES_* value. The new check->health is computed based on the
216 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200217 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200218 * Shows information in logs about failed health check if server is UP or
219 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200220 */
Simon Horman4a741432013-02-23 15:35:38 +0900221static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100222{
Simon Horman4a741432013-02-23 15:35:38 +0900223 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200224 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200225 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900226
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200227 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100228 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900229 check->desc[0] = '\0';
230 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200231 return;
232 }
233
Simon Horman4a741432013-02-23 15:35:38 +0900234 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200235 return;
236
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200237 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900238 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
239 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200240 } else
Simon Horman4a741432013-02-23 15:35:38 +0900241 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200242
Simon Horman4a741432013-02-23 15:35:38 +0900243 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200244 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900245 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200246
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100247 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900248 check->duration = -1;
249 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200250 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900251 check->duration = tv_ms_elapsed(&check->start, &now);
252 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200253 }
254
Willy Tarreau23964182014-05-20 20:56:30 +0200255 /* no change is expected if no state change occurred */
256 if (check->result == CHK_RES_NEUTRAL)
257 return;
258
Olivier Houchard0923fa42019-01-11 18:43:04 +0100259 /* If the check was really just sending a mail, it won't have an
260 * associated server, so we're done now.
261 */
262 if (!s)
263 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200265
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200266 switch (check->result) {
267 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200268 /* Failure to connect to the agent as a secondary check should not
269 * cause the server to be marked down.
270 */
271 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900272 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200273 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100274 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200275 report = 1;
276 check->health--;
277 if (check->health < check->rise)
278 check->health = 0;
279 }
280 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200281
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200282 case CHK_RES_PASSED:
283 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
284 if ((check->health < check->rise + check->fall - 1) &&
285 (check->result == CHK_RES_PASSED || check->health > 0)) {
286 report = 1;
287 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200288
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200289 if (check->health >= check->rise)
290 check->health = check->rise + check->fall - 1; /* OK now */
291 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200292
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200293 /* clear consecutive_errors if observing is enabled */
294 if (s->onerror)
295 s->consecutive_errors = 0;
296 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100297
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200298 default:
299 break;
300 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200301
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200302 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
303 (status != prev_status || report)) {
304 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200305 "%s check for %sserver %s/%s %s%s",
306 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200307 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100308 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100309 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200310 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311
Emeric Brun5a133512017-10-19 14:42:30 +0200312 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200313
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100314 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200315 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
316 (check->health >= check->rise) ? check->fall : check->rise,
317 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200318
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200319 ha_warning("%s.\n", trash.area);
320 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
321 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200322 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200323}
324
Willy Tarreau4eec5472014-05-20 22:32:27 +0200325/* Marks the check <check>'s server down if the current check is already failed
326 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200327 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200328static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200329{
Simon Horman4a741432013-02-23 15:35:38 +0900330 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900331
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200332 /* The agent secondary check should only cause a server to be marked
333 * as down if check->status is HCHK_STATUS_L7STS, which indicates
334 * that the agent returned "fail", "stopped" or "down".
335 * The implication here is that failure to connect to the agent
336 * as a secondary check should not cause the server to be marked
337 * down. */
338 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
339 return;
340
Willy Tarreau4eec5472014-05-20 22:32:27 +0200341 if (check->health > 0)
342 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100343
Willy Tarreau4eec5472014-05-20 22:32:27 +0200344 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200345 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200346}
347
Willy Tarreauaf549582014-05-16 17:37:50 +0200348/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200349 * it isn't in maintenance, it is not tracking a down server and other checks
350 * comply. The rule is simple : by default, a server is up, unless any of the
351 * following conditions is true :
352 * - health check failed (check->health < rise)
353 * - agent check failed (agent->health < rise)
354 * - the server tracks a down server (track && track->state == STOPPED)
355 * Note that if the server has a slowstart, it will switch to STARTING instead
356 * of RUNNING. Also, only the health checks support the nolb mode, so the
357 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200358 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200359static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200360{
Simon Horman4a741432013-02-23 15:35:38 +0900361 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100362
Emeric Brun52a91d32017-08-31 14:41:55 +0200363 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200364 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100365
Emeric Brun52a91d32017-08-31 14:41:55 +0200366 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200367 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368
Willy Tarreau3e048382014-05-21 10:30:54 +0200369 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
370 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100371
Willy Tarreau3e048382014-05-21 10:30:54 +0200372 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
373 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200374
Emeric Brun52a91d32017-08-31 14:41:55 +0200375 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200376 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100377
Emeric Brun5a133512017-10-19 14:42:30 +0200378 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100379}
380
Willy Tarreaudb58b792014-05-21 13:57:23 +0200381/* Marks the check <check> as valid and tries to set its server into stopping mode
382 * if it was running or starting, and provided it isn't in maintenance and other
383 * checks comply. The conditions for the server to be marked in stopping mode are
384 * the same as for it to be turned up. Also, only the health checks support the
385 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200386 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200387static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200388{
Simon Horman4a741432013-02-23 15:35:38 +0900389 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100390
Emeric Brun52a91d32017-08-31 14:41:55 +0200391 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200392 return;
393
Willy Tarreaudb58b792014-05-21 13:57:23 +0200394 if (check->state & CHK_ST_AGENT)
395 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396
Emeric Brun52a91d32017-08-31 14:41:55 +0200397 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200398 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100399
Willy Tarreaudb58b792014-05-21 13:57:23 +0200400 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
401 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100402
Willy Tarreaudb58b792014-05-21 13:57:23 +0200403 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
404 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100405
Willy Tarreaub26881a2017-12-23 11:16:49 +0100406 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100407}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200408
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100409/* note: use health_adjust() only, which first checks that the observe mode is
410 * enabled.
411 */
412void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100413{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100414 int failed;
415 int expire;
416
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100417 if (s->observe >= HANA_OBS_SIZE)
418 return;
419
Willy Tarreaubb956662013-01-24 00:37:39 +0100420 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100421 return;
422
423 switch (analyze_statuses[status].lr[s->observe - 1]) {
424 case 1:
425 failed = 1;
426 break;
427
428 case 2:
429 failed = 0;
430 break;
431
432 default:
433 return;
434 }
435
436 if (!failed) {
437 /* good: clear consecutive_errors */
438 s->consecutive_errors = 0;
439 return;
440 }
441
Olivier Houchard7059c552019-03-08 18:49:32 +0100442 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100443
444 if (s->consecutive_errors < s->consecutive_errors_limit)
445 return;
446
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100447 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
448 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100449
450 switch (s->onerror) {
451 case HANA_ONERR_FASTINTER:
452 /* force fastinter - nothing to do here as all modes force it */
453 break;
454
455 case HANA_ONERR_SUDDTH:
456 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900457 if (s->check.health > s->check.rise)
458 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100459
460 /* no break - fall through */
461
462 case HANA_ONERR_FAILCHK:
463 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200464 set_server_check_status(&s->check, HCHK_STATUS_HANA,
465 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200466 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100467 break;
468
469 case HANA_ONERR_MARKDWN:
470 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900471 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200472 set_server_check_status(&s->check, HCHK_STATUS_HANA,
473 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200474 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100475 break;
476
477 default:
478 /* write a warning? */
479 break;
480 }
481
482 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100483 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100484
Simon Horman66183002013-02-23 10:16:43 +0900485 if (s->check.fastinter) {
486 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300487 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200488 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300489 /* requeue check task with new expire */
490 task_queue(s->check.task);
491 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100492 }
Willy Tarreauef781042010-01-27 11:53:01 +0100493}
494
Christopher Faulet61cc8522020-04-20 14:54:42 +0200495/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100496 * closed, keep errno intact as it is supposed to contain the valid error code.
497 * If no error is reported, check the socket's error queue using getsockopt().
498 * Warning, this must be done only once when returning from poll, and never
499 * after an I/O error was attempted, otherwise the error queue might contain
500 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
501 * socket. Returns non-zero if an error was reported, zero if everything is
502 * clean (including a properly closed socket).
503 */
504static int retrieve_errno_from_socket(struct connection *conn)
505{
506 int skerr;
507 socklen_t lskerr = sizeof(skerr);
508
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100509 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100510 return 1;
511
Willy Tarreau3c728722014-01-23 13:50:42 +0100512 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100513 return 0;
514
Willy Tarreau585744b2017-08-24 14:31:19 +0200515 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100516 errno = skerr;
517
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100518 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100519
520 if (!errno) {
521 /* we could not retrieve an error, that does not mean there is
522 * none. Just don't change anything and only report the prior
523 * error if any.
524 */
525 if (conn->flags & CO_FL_ERROR)
526 return 1;
527 else
528 return 0;
529 }
530
531 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
532 return 1;
533}
534
Christopher Faulet61cc8522020-04-20 14:54:42 +0200535/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100536 * and adjust the server status accordingly. It may make use of <errno_bck>
537 * if non-null when the caller is absolutely certain of its validity (eg:
538 * checked just after a syscall). If the caller doesn't have a valid errno,
539 * it can pass zero, and retrieve_errno_from_socket() will be called to try
540 * to extract errno from the socket. If no error is reported, it will consider
541 * the <expired> flag. This is intended to be used when a connection error was
542 * reported in conn->flags or when a timeout was reported in <expired>. The
543 * function takes care of not updating a server status which was already set.
544 * All situations where at least one of <expired> or CO_FL_ERROR are set
545 * produce a status.
546 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200547static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200549 struct conn_stream *cs = check->cs;
550 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100551 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200552 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200553 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100555 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100556 return;
557
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100558 errno = unclean_errno(errno_bck);
559 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100560 retrieve_errno_from_socket(conn);
561
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200562 if (conn && !(conn->flags & CO_FL_ERROR) &&
563 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100564 return;
565
566 /* we'll try to build a meaningful error message depending on the
567 * context of the error possibly present in conn->err_code, and the
568 * socket error possibly collected above. This is useful to know the
569 * exact step of the L6 layer (eg: SSL handshake).
570 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200571 chk = get_trash_chunk();
572
Christopher Faulet799f3a42020-04-07 12:06:14 +0200573 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200574 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200575 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200576 if (!step)
577 chunk_printf(chk, " at initial connection step of tcp-check");
578 else {
579 chunk_printf(chk, " at step %d of tcp-check", step);
580 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200581 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
582 if (check->current_step->connect.port)
583 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200584 else
585 chunk_appendf(chk, " (connect)");
586 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200587 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
588 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100589
590 switch (expect->type) {
591 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200592 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100593 break;
594 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200595 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100596 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200597 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200598 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100599 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200600 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100601 chunk_appendf(chk, " (expect binary regex)");
602 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200603 case TCPCHK_EXPECT_STRING_LF:
604 chunk_appendf(chk, " (expect log-format string)");
605 break;
606 case TCPCHK_EXPECT_BINARY_LF:
607 chunk_appendf(chk, " (expect log-format binary)");
608 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200610 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200612 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200613 chunk_appendf(chk, " (expect HTTP status regex)");
614 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200615 case TCPCHK_EXPECT_HTTP_HEADER:
616 chunk_appendf(chk, " (expect HTTP header pattern)");
617 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200618 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200619 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200620 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200621 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200622 chunk_appendf(chk, " (expect HTTP body regex)");
623 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200624 case TCPCHK_EXPECT_HTTP_BODY_LF:
625 chunk_appendf(chk, " (expect log-format HTTP body)");
626 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200627 case TCPCHK_EXPECT_CUSTOM:
628 chunk_appendf(chk, " (expect custom function)");
629 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100630 case TCPCHK_EXPECT_UNDEF:
631 chunk_appendf(chk, " (undefined expect!)");
632 break;
633 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200634 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200635 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200636 chunk_appendf(chk, " (send)");
637 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200638
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200639 if (check->current_step && check->current_step->comment)
640 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200641 }
642 }
643
Willy Tarreau00149122017-10-04 18:05:01 +0200644 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100645 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200646 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
647 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100648 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
650 chk->area);
651 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100652 }
653 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100654 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200655 chunk_printf(&trash, "%s%s", strerror(errno),
656 chk->area);
657 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100658 }
659 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200660 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100661 }
662 }
663
Willy Tarreau00149122017-10-04 18:05:01 +0200664 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200665 /* NOTE: this is reported after <fall> tries */
666 chunk_printf(chk, "No port available for the TCP connection");
667 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
668 }
669
Willy Tarreau00149122017-10-04 18:05:01 +0200670 if (!conn) {
671 /* connection allocation error before the connection was established */
672 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
673 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100674 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100675 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200676 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100677 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
678 else if (expired)
679 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200680
681 /*
682 * might be due to a server IP change.
683 * Let's trigger a DNS resolution if none are currently running.
684 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100685 if (check->server)
686 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200687
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100688 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100689 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100690 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200691 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100692 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
693 else if (expired)
694 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
695 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200696 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 /* I/O error after connection was established and before we could diagnose */
698 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
699 }
700 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200701 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
702
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100703 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200704 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
705 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200706 tout = check->current_step->expect.tout_status;
707 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100708 }
709
710 return;
711}
712
Willy Tarreaubaaee002006-06-26 02:48:02 +0200713
Christopher Faulet61cc8522020-04-20 14:54:42 +0200714/**************************************************************************/
715/*************** Init/deinit tcp-check rules and ruleset ******************/
716/**************************************************************************/
717/* Releases memory allocated for a log-format string */
718static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200719{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200720 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100721
Christopher Faulet61cc8522020-04-20 14:54:42 +0200722 list_for_each_entry_safe(lf, lfb, fmt, list) {
723 LIST_DEL(&lf->list);
724 release_sample_expr(lf->expr);
725 free(lf->arg);
726 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100727 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200728}
729
Christopher Faulet61cc8522020-04-20 14:54:42 +0200730/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
731static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100732{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200733 if (!hdr)
734 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200735
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200737 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200738 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100739}
740
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741/* Releases memory allocated for an HTTP header list used in a tcp-check send
742 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200743 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200744static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200745{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200746 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200747
Christopher Faulet61cc8522020-04-20 14:54:42 +0200748 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
749 LIST_DEL(&hdr->list);
750 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200751 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200752}
753
Christopher Faulet61cc8522020-04-20 14:54:42 +0200754/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
755 * tcp-check was allocated using a memory pool (it is used to instantiate email
756 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200757 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200758static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200759{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200760 if (!rule)
761 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200762
Christopher Faulet61cc8522020-04-20 14:54:42 +0200763 free(rule->comment);
764 switch (rule->action) {
765 case TCPCHK_ACT_SEND:
766 switch (rule->send.type) {
767 case TCPCHK_SEND_STRING:
768 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200769 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200770 break;
771 case TCPCHK_SEND_STRING_LF:
772 case TCPCHK_SEND_BINARY_LF:
773 free_tcpcheck_fmt(&rule->send.fmt);
774 break;
775 case TCPCHK_SEND_HTTP:
776 free(rule->send.http.meth.str.area);
777 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200778 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200779 else
780 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200781 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200782 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
783 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200784 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200785 else
786 free_tcpcheck_fmt(&rule->send.http.body_fmt);
787 break;
788 case TCPCHK_SEND_UNDEF:
789 break;
790 }
791 break;
792 case TCPCHK_ACT_EXPECT:
793 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
794 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
795 release_sample_expr(rule->expect.status_expr);
796 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200797 case TCPCHK_EXPECT_HTTP_STATUS:
798 free(rule->expect.codes.codes);
799 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200800 case TCPCHK_EXPECT_STRING:
801 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200802 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200803 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200804 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200805 case TCPCHK_EXPECT_STRING_REGEX:
806 case TCPCHK_EXPECT_BINARY_REGEX:
807 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
808 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200809 regex_free(rule->expect.regex);
810 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200811 case TCPCHK_EXPECT_STRING_LF:
812 case TCPCHK_EXPECT_BINARY_LF:
813 case TCPCHK_EXPECT_HTTP_BODY_LF:
814 free_tcpcheck_fmt(&rule->expect.fmt);
815 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200816 case TCPCHK_EXPECT_HTTP_HEADER:
817 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
818 regex_free(rule->expect.hdr.name_re);
819 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
820 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
821 else
822 istfree(&rule->expect.hdr.name);
823
824 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
825 regex_free(rule->expect.hdr.value_re);
826 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
827 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
828 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
829 istfree(&rule->expect.hdr.value);
830 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200831 case TCPCHK_EXPECT_CUSTOM:
832 case TCPCHK_EXPECT_UNDEF:
833 break;
834 }
835 break;
836 case TCPCHK_ACT_CONNECT:
837 free(rule->connect.sni);
838 free(rule->connect.alpn);
839 release_sample_expr(rule->connect.port_expr);
840 break;
841 case TCPCHK_ACT_COMMENT:
842 break;
843 case TCPCHK_ACT_ACTION_KW:
844 free(rule->action_kw.rule);
845 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200846 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200847
848 if (in_pool)
849 pool_free(pool_head_tcpcheck_rule, rule);
850 else
851 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200852}
853
Christopher Faulet61cc8522020-04-20 14:54:42 +0200854/* Creates a tcp-check variable used in preset variables before executing a
855 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100856 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200857static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100858{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200859 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100860
Christopher Faulet61cc8522020-04-20 14:54:42 +0200861 var = calloc(1, sizeof(*var));
862 if (var == NULL)
863 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100864
Christopher Fauletb61caf42020-04-21 10:57:42 +0200865 var->name = istdup(name);
866 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200867 free(var);
868 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100869 }
Simon Horman98637e52014-06-20 12:30:16 +0900870
Christopher Faulet61cc8522020-04-20 14:54:42 +0200871 LIST_INIT(&var->list);
872 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900873}
874
Christopher Faulet61cc8522020-04-20 14:54:42 +0200875/* Releases memory allocated for a preset tcp-check variable */
876static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900877{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200878 if (!var)
879 return;
880
Christopher Fauletb61caf42020-04-21 10:57:42 +0200881 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200882 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
883 free(var->data.u.str.area);
884 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
885 free(var->data.u.meth.str.area);
886 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900887}
888
Christopher Faulet61cc8522020-04-20 14:54:42 +0200889/* Releases a list of preset tcp-check variables */
890static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900891{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200892 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200893
Christopher Faulet61cc8522020-04-20 14:54:42 +0200894 list_for_each_entry_safe(var, back, vars, list) {
895 LIST_DEL(&var->list);
896 free_tcpcheck_var(var);
897 }
Simon Horman98637e52014-06-20 12:30:16 +0900898}
899
Christopher Faulet61cc8522020-04-20 14:54:42 +0200900/* Duplicate a list of preset tcp-check variables */
901int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900902{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900904
Christopher Faulet61cc8522020-04-20 14:54:42 +0200905 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200906 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200907 if (!new)
908 goto error;
909 new->data.type = var->data.type;
910 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
911 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
912 goto error;
913 if (var->data.type == SMP_T_STR)
914 new->data.u.str.area[new->data.u.str.data] = 0;
915 }
916 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
917 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
918 goto error;
919 new->data.u.str.area[new->data.u.str.data] = 0;
920 new->data.u.meth.meth = var->data.u.meth.meth;
921 }
922 else
923 new->data.u = var->data.u;
924 LIST_ADDQ(dst, &new->list);
925 }
926 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900927
Christopher Faulet61cc8522020-04-20 14:54:42 +0200928 error:
929 free(new);
930 return 0;
931}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200932
Christopher Faulet61cc8522020-04-20 14:54:42 +0200933/* Looks for a shared tcp-check ruleset given its name. */
934static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
935{
936 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200937 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900938
Christopher Fauletd7cee712020-04-21 13:45:00 +0200939 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
940 if (node) {
941 rs = container_of(node, typeof(*rs), node);
942 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200943 }
944 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900945}
946
Christopher Faulet61cc8522020-04-20 14:54:42 +0200947/* Creates a new shared tcp-check ruleset */
948static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900949{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900951
Christopher Faulet61cc8522020-04-20 14:54:42 +0200952 rs = calloc(1, sizeof(*rs));
953 if (rs == NULL)
954 return NULL;
955
Christopher Fauletd7cee712020-04-21 13:45:00 +0200956 rs->node.key = strdup(name);
957 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200958 free(rs);
959 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900960 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200963 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900965}
966
Christopher Faulet61cc8522020-04-20 14:54:42 +0200967/* Releases memory allocated by a tcp-check ruleset. */
968static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900969{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200970 struct tcpcheck_rule *r, *rb;
971 if (!rs)
972 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200973
Christopher Fauletd7cee712020-04-21 13:45:00 +0200974 ebpt_delete(&rs->node);
975 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200976 list_for_each_entry_safe(r, rb, &rs->rules, list) {
977 LIST_DEL(&r->list);
978 free_tcpcheck(r, 0);
979 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200980 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900981}
982
Christopher Faulet61cc8522020-04-20 14:54:42 +0200983
984/**************************************************************************/
985/**************** Everything about tcp-checks execution *******************/
986/**************************************************************************/
987/* Returns the id of a step in a tcp-check ruleset */
988static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200989{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200990 if (!rule)
991 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900992
Christopher Faulet61cc8522020-04-20 14:54:42 +0200993 /* no last started step => first step */
994 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900995 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900996
Christopher Faulet61cc8522020-04-20 14:54:42 +0200997 /* last step is the first implicit connect */
998 if (rule->index == 0 &&
999 rule->action == TCPCHK_ACT_CONNECT &&
1000 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1001 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001002
Christopher Faulet61cc8522020-04-20 14:54:42 +02001003 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001004}
1005
Christopher Faulet61cc8522020-04-20 14:54:42 +02001006/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1007 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001008 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001010{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001011 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001012
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 list_for_each_entry(r, rules->list, list) {
1014 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1015 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001016 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001017 return NULL;
1018}
Cyril Bontéac92a062014-12-27 22:28:38 +01001019
Christopher Faulet61cc8522020-04-20 14:54:42 +02001020/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1021 * NULL if none was found.
1022 */
1023static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1024{
1025 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001026
Christopher Faulet61cc8522020-04-20 14:54:42 +02001027 list_for_each_entry_rev(r, rules->list, list) {
1028 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1029 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001030 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001031 return NULL;
1032}
Cyril Bontéac92a062014-12-27 22:28:38 +01001033
Christopher Faulet61cc8522020-04-20 14:54:42 +02001034/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1035 * <start> or NULL if non was found. If <start> is NULL, it relies on
1036 * get_first_tcpcheck_rule().
1037 */
1038static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1039{
1040 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001041
Christopher Faulet61cc8522020-04-20 14:54:42 +02001042 if (!start)
1043 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001044
Christopher Faulet61cc8522020-04-20 14:54:42 +02001045 r = LIST_NEXT(&start->list, typeof(r), list);
1046 list_for_each_entry_from(r, rules->list, list) {
1047 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1048 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001049 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001051}
Simon Horman98637e52014-06-20 12:30:16 +09001052
Simon Horman98637e52014-06-20 12:30:16 +09001053
Christopher Faulet61cc8522020-04-20 14:54:42 +02001054/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1055static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1056 int match, struct ist info)
1057{
1058 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001059
Christopher Faulet61cc8522020-04-20 14:54:42 +02001060 /* Follows these step to produce the info message:
1061 * 1. if info field is already provided, copy it
1062 * 2. if the expect rule provides an onerror log-format string,
1063 * use it to produce the message
1064 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1065 * 4. Otherwise produce the generic tcp-check info message
1066 */
1067 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001068 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001070 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001071 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1072 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1073 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001074 }
Simon Horman98637e52014-06-20 12:30:16 +09001075
Christopher Faulet61cc8522020-04-20 14:54:42 +02001076 if (check->type == PR_O2_TCPCHK_CHK &&
1077 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1078 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001079
Christopher Faulet61cc8522020-04-20 14:54:42 +02001080 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1081 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001082 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001083 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1084 break;
1085 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001087 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001088 tcpcheck_get_step_id(check, rule));
1089 break;
1090 case TCPCHK_EXPECT_BINARY:
1091 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1092 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001093 case TCPCHK_EXPECT_STRING_REGEX:
1094 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1095 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001096 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1097 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001098 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001100 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001101 case TCPCHK_EXPECT_STRING_LF:
1102 case TCPCHK_EXPECT_HTTP_BODY_LF:
1103 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1104 break;
1105 case TCPCHK_EXPECT_BINARY_LF:
1106 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1107 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001108 case TCPCHK_EXPECT_CUSTOM:
1109 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1110 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001111 case TCPCHK_EXPECT_HTTP_HEADER:
1112 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001113 case TCPCHK_EXPECT_UNDEF:
1114 /* Should never happen. */
1115 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001116 }
1117
Christopher Faulet61cc8522020-04-20 14:54:42 +02001118 comment:
1119 /* If the failing expect rule provides a comment, it is concatenated to
1120 * the info message.
1121 */
1122 if (rule->comment) {
1123 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001124 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001125 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001126
Christopher Faulet61cc8522020-04-20 14:54:42 +02001127 /* Finally, the check status code is set if the failing expect rule
1128 * defines a status expression.
1129 */
1130 if (rule->expect.status_expr) {
1131 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001132 rule->expect.status_expr, SMP_T_STR);
1133
1134 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1135 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001137 }
Simon Horman98637e52014-06-20 12:30:16 +09001138
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 *(b_tail(msg)) = '\0';
1140}
Cyril Bontéac92a062014-12-27 22:28:38 +01001141
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1143static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1144 struct ist info)
1145{
1146 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001147
Christopher Faulet61cc8522020-04-20 14:54:42 +02001148 /* Follows these step to produce the info message:
1149 * 1. if info field is already provided, copy it
1150 * 2. if the expect rule provides an onsucces log-format string,
1151 * use it to produce the message
1152 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1153 * 4. Otherwise produce the generic tcp-check info message
1154 */
1155 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001156 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001157 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1158 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1159 &rule->expect.onsuccess_fmt);
1160 else if (check->type == PR_O2_TCPCHK_CHK &&
1161 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1162 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001163
Christopher Faulet61cc8522020-04-20 14:54:42 +02001164 /* Finally, the check status code is set if the expect rule defines a
1165 * status expression.
1166 */
1167 if (rule->expect.status_expr) {
1168 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001169 rule->expect.status_expr, SMP_T_STR);
1170
1171 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1172 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001173 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001174 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001175
1176 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001177}
1178
Christopher Faulet61cc8522020-04-20 14:54:42 +02001179/* Builds the server state header used by HTTP health-checks */
1180static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001181{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001182 int sv_state;
1183 int ratio;
1184 char addr[46];
1185 char port[6];
1186 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1187 "UP %d/%d", "UP",
1188 "NOLB %d/%d", "NOLB",
1189 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001190
Christopher Faulet61cc8522020-04-20 14:54:42 +02001191 if (!(s->check.state & CHK_ST_ENABLED))
1192 sv_state = 6;
1193 else if (s->cur_state != SRV_ST_STOPPED) {
1194 if (s->check.health == s->check.rise + s->check.fall - 1)
1195 sv_state = 3; /* UP */
1196 else
1197 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001198
Christopher Faulet61cc8522020-04-20 14:54:42 +02001199 if (s->cur_state == SRV_ST_STOPPING)
1200 sv_state += 2;
1201 } else {
1202 if (s->check.health)
1203 sv_state = 1; /* going up */
1204 else
1205 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001206 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001207
Christopher Faulet61cc8522020-04-20 14:54:42 +02001208 chunk_appendf(buf, srv_hlt_st[sv_state],
1209 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1210 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001211
Christopher Faulet61cc8522020-04-20 14:54:42 +02001212 addr_to_str(&s->addr, addr, sizeof(addr));
1213 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1214 snprintf(port, sizeof(port), "%u", s->svc_port);
1215 else
1216 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001217
Christopher Faulet61cc8522020-04-20 14:54:42 +02001218 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1219 addr, port, s->proxy->id, s->id,
1220 global.node,
1221 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1222 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1223 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1224 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001225
Christopher Faulet61cc8522020-04-20 14:54:42 +02001226 if ((s->cur_state == SRV_ST_STARTING) &&
1227 now.tv_sec < s->last_change + s->slowstart &&
1228 now.tv_sec >= s->last_change) {
1229 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1230 chunk_appendf(buf, "; throttle=%d%%", ratio);
1231 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 return b_data(buf);
1234}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236/* Internal functions to parse and validate a MySQL packet in the context of an
1237 * expect rule. It start to parse the input buffer at the offset <offset>. If
1238 * <last_read> is set, no more data are expected.
1239 */
1240static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1241 unsigned int offset, int last_read)
1242{
1243 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1244 enum healthcheck_status status;
1245 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001246 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001247 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001248
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001249
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001251 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001252 if (!last_read)
1253 goto wait_more_data;
1254
1255 /* invalid length or truncated response */
1256 status = HCHK_STATUS_L7RSP;
1257 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001258 }
1259
Christopher Faulet61cc8522020-04-20 14:54:42 +02001260 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1261 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1262 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001263
Christopher Faulet61cc8522020-04-20 14:54:42 +02001264 if (b_data(&check->bi) < offset+plen+4) {
1265 if (!last_read)
1266 goto wait_more_data;
1267
1268 /* invalid length or truncated response */
1269 status = HCHK_STATUS_L7RSP;
1270 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001271 }
Simon Horman98637e52014-06-20 12:30:16 +09001272
Christopher Faulet61cc8522020-04-20 14:54:42 +02001273 if (*b_peek(&check->bi, offset+4) == '\xff') {
1274 /* MySQL Error packet always begin with field_count = 0xff */
1275 status = HCHK_STATUS_L7STS;
1276 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1277 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1278 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1279 goto error;
1280 }
Simon Horman98637e52014-06-20 12:30:16 +09001281
Christopher Faulet61cc8522020-04-20 14:54:42 +02001282 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1283 /* Not the last rule, continue */
1284 goto out;
1285 }
Simon Horman98637e52014-06-20 12:30:16 +09001286
Christopher Faulet61cc8522020-04-20 14:54:42 +02001287 /* We set the MySQL Version in description for information purpose
1288 * FIXME : it can be cool to use MySQL Version for other purpose,
1289 * like mark as down old MySQL server.
1290 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001291 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1292 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001293
Christopher Faulet61cc8522020-04-20 14:54:42 +02001294 out:
1295 free_trash_chunk(msg);
1296 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001297
Christopher Faulet61cc8522020-04-20 14:54:42 +02001298 error:
1299 ret = TCPCHK_EVAL_STOP;
1300 check->code = err;
1301 msg = alloc_trash_chunk();
1302 if (msg)
1303 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1304 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1305 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001306
Christopher Faulet61cc8522020-04-20 14:54:42 +02001307 wait_more_data:
1308 ret = TCPCHK_EVAL_WAIT;
1309 goto out;
1310}
Simon Horman98637e52014-06-20 12:30:16 +09001311
Christopher Faulet61cc8522020-04-20 14:54:42 +02001312/* Custom tcp-check expect function to parse and validate the MySQL initial
1313 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1314 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1315 * error occurred.
1316 */
1317static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1318{
1319 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1320}
Simon Horman98637e52014-06-20 12:30:16 +09001321
Christopher Faulet61cc8522020-04-20 14:54:42 +02001322/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1323 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1324 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1325 * an error occurred.
1326 */
1327static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1328{
1329 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001330
Christopher Faulet61cc8522020-04-20 14:54:42 +02001331 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1332 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1333 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1336}
Simon Horman98637e52014-06-20 12:30:16 +09001337
Christopher Faulet61cc8522020-04-20 14:54:42 +02001338/* Custom tcp-check expect function to parse and validate the LDAP bind response
1339 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1340 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1341 * error occurred.
1342 */
1343static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1344{
1345 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1346 enum healthcheck_status status;
1347 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001348 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001350
Christopher Faulet61cc8522020-04-20 14:54:42 +02001351 /* Check if the server speaks LDAP (ASN.1/BER)
1352 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1353 * http://tools.ietf.org/html/rfc4511
1354 */
1355 /* size of LDAPMessage */
1356 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001357
Christopher Faulet61cc8522020-04-20 14:54:42 +02001358 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1359 * messageID: 0x02 0x01 0x01: INTEGER 1
1360 * protocolOp: 0x61: bindResponse
1361 */
1362 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1363 status = HCHK_STATUS_L7RSP;
1364 desc = ist("Not LDAPv3 protocol");
1365 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001366 }
Simon Horman98637e52014-06-20 12:30:16 +09001367
Christopher Faulet61cc8522020-04-20 14:54:42 +02001368 /* size of bindResponse */
1369 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001370
Christopher Faulet61cc8522020-04-20 14:54:42 +02001371 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1372 * ldapResult: 0x0a 0x01: ENUMERATION
1373 */
1374 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1375 status = HCHK_STATUS_L7RSP;
1376 desc = ist("Not LDAPv3 protocol");
1377 goto error;
1378 }
Simon Horman98637e52014-06-20 12:30:16 +09001379
Christopher Faulet61cc8522020-04-20 14:54:42 +02001380 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1381 * resultCode
1382 */
1383 check->code = *(b_head(&check->bi) + msglen + 9);
1384 if (check->code) {
1385 status = HCHK_STATUS_L7STS;
1386 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1387 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001388 }
1389
Christopher Faulet1941bab2020-05-05 07:55:50 +02001390 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1391 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001392
Christopher Faulet61cc8522020-04-20 14:54:42 +02001393 out:
1394 free_trash_chunk(msg);
1395 return ret;
1396
1397 error:
1398 ret = TCPCHK_EVAL_STOP;
1399 msg = alloc_trash_chunk();
1400 if (msg)
1401 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1402 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1403 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001404}
1405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1407 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1408 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001409 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001411{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1413 enum healthcheck_status status;
1414 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001415 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001417
Willy Tarreaubaaee002006-06-26 02:48:02 +02001418
Christopher Faulet61cc8522020-04-20 14:54:42 +02001419 memcpy(&framesz, b_head(&check->bi), 4);
1420 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001421
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 if (!last_read && b_data(&check->bi) < (4+framesz))
1423 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001424
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425 memset(b_orig(&trash), 0, b_size(&trash));
1426 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1427 status = HCHK_STATUS_L7RSP;
1428 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1429 goto error;
1430 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001431
Christopher Faulet1941bab2020-05-05 07:55:50 +02001432 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1433 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001434
Christopher Faulet61cc8522020-04-20 14:54:42 +02001435 out:
1436 free_trash_chunk(msg);
1437 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001438
Christopher Faulet61cc8522020-04-20 14:54:42 +02001439 error:
1440 ret = TCPCHK_EVAL_STOP;
1441 msg = alloc_trash_chunk();
1442 if (msg)
1443 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1444 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1445 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001446
Christopher Faulet61cc8522020-04-20 14:54:42 +02001447 wait_more_data:
1448 ret = TCPCHK_EVAL_WAIT;
1449 goto out;
1450}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001451
Christopher Faulet61cc8522020-04-20 14:54:42 +02001452/* Custom tcp-check expect function to parse and validate the agent-check
1453 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1454 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1455 */
1456static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1457{
1458 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1459 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1460 const char *hs = NULL; /* health status */
1461 const char *as = NULL; /* admin status */
1462 const char *ps = NULL; /* performance status */
1463 const char *cs = NULL; /* maxconn */
1464 const char *err = NULL; /* first error to report */
1465 const char *wrn = NULL; /* first warning to report */
1466 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001467
Christopher Faulet61cc8522020-04-20 14:54:42 +02001468 /* We're getting an agent check response. The agent could
1469 * have been disabled in the mean time with a long check
1470 * still pending. It is important that we ignore the whole
1471 * response.
1472 */
1473 if (!(check->state & CHK_ST_ENABLED))
1474 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001475
Christopher Faulet61cc8522020-04-20 14:54:42 +02001476 /* The agent supports strings made of a single line ended by the
1477 * first CR ('\r') or LF ('\n'). This line is composed of words
1478 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1479 * line may optionally contained a description of a state change
1480 * after a sharp ('#'), which is only considered if a health state
1481 * is announced.
1482 *
1483 * Words may be composed of :
1484 * - a numeric weight suffixed by the percent character ('%').
1485 * - a health status among "up", "down", "stopped", and "fail".
1486 * - an admin status among "ready", "drain", "maint".
1487 *
1488 * These words may appear in any order. If multiple words of the
1489 * same category appear, the last one wins.
1490 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001491
Christopher Faulet61cc8522020-04-20 14:54:42 +02001492 p = b_head(&check->bi);
1493 while (*p && *p != '\n' && *p != '\r')
1494 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001495
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496 if (!*p) {
1497 if (!last_read)
1498 goto wait_more_data;
1499
1500 /* at least inform the admin that the agent is mis-behaving */
1501 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1502 goto out;
1503 }
1504
1505 *p = 0;
1506 cmd = b_head(&check->bi);
1507
1508 while (*cmd) {
1509 /* look for next word */
1510 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1511 cmd++;
1512 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001513 }
1514
Christopher Faulet61cc8522020-04-20 14:54:42 +02001515 if (*cmd == '#') {
1516 /* this is the beginning of a health status description,
1517 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001518 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001519 cmd++;
1520 while (*cmd == '\t' || *cmd == ' ')
1521 cmd++;
1522 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001523 }
1524
Christopher Faulet61cc8522020-04-20 14:54:42 +02001525 /* find the end of the word so that we have a null-terminated
1526 * word between <cmd> and <p>.
1527 */
1528 p = cmd + 1;
1529 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1530 p++;
1531 if (*p)
1532 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001533
Christopher Faulet61cc8522020-04-20 14:54:42 +02001534 /* first, health statuses */
1535 if (strcasecmp(cmd, "up") == 0) {
1536 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1537 status = HCHK_STATUS_L7OKD;
1538 hs = cmd;
1539 }
1540 else if (strcasecmp(cmd, "down") == 0) {
1541 check->server->check.health = 0;
1542 status = HCHK_STATUS_L7STS;
1543 hs = cmd;
1544 }
1545 else if (strcasecmp(cmd, "stopped") == 0) {
1546 check->server->check.health = 0;
1547 status = HCHK_STATUS_L7STS;
1548 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001549 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001550 else if (strcasecmp(cmd, "fail") == 0) {
1551 check->server->check.health = 0;
1552 status = HCHK_STATUS_L7STS;
1553 hs = cmd;
1554 }
1555 /* admin statuses */
1556 else if (strcasecmp(cmd, "ready") == 0) {
1557 as = cmd;
1558 }
1559 else if (strcasecmp(cmd, "drain") == 0) {
1560 as = cmd;
1561 }
1562 else if (strcasecmp(cmd, "maint") == 0) {
1563 as = cmd;
1564 }
1565 /* try to parse a weight here and keep the last one */
1566 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1567 ps = cmd;
1568 }
1569 /* try to parse a maxconn here */
1570 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1571 cs = cmd;
1572 }
1573 else {
1574 /* keep a copy of the first error */
1575 if (!err)
1576 err = cmd;
1577 }
1578 /* skip to next word */
1579 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001580 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001581 /* here, cmd points either to \0 or to the beginning of a
1582 * description. Skip possible leading spaces.
1583 */
1584 while (*cmd == ' ' || *cmd == '\n')
1585 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001586
Christopher Faulet61cc8522020-04-20 14:54:42 +02001587 /* First, update the admin status so that we avoid sending other
1588 * possibly useless warnings and can also update the health if
1589 * present after going back up.
1590 */
1591 if (as) {
1592 if (strcasecmp(as, "drain") == 0)
1593 srv_adm_set_drain(check->server);
1594 else if (strcasecmp(as, "maint") == 0)
1595 srv_adm_set_maint(check->server);
1596 else
1597 srv_adm_set_ready(check->server);
1598 }
Simon Horman98637e52014-06-20 12:30:16 +09001599
Christopher Faulet61cc8522020-04-20 14:54:42 +02001600 /* now change weights */
1601 if (ps) {
1602 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001603
Christopher Faulet61cc8522020-04-20 14:54:42 +02001604 msg = server_parse_weight_change_request(check->server, ps);
1605 if (!wrn || !*wrn)
1606 wrn = msg;
1607 }
Simon Horman98637e52014-06-20 12:30:16 +09001608
Christopher Faulet61cc8522020-04-20 14:54:42 +02001609 if (cs) {
1610 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001611
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001613
Christopher Faulet61cc8522020-04-20 14:54:42 +02001614 msg = server_parse_maxconn_change_request(check->server, cs);
1615 if (!wrn || !*wrn)
1616 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001617 }
1618
Christopher Faulet61cc8522020-04-20 14:54:42 +02001619 /* and finally health status */
1620 if (hs) {
1621 /* We'll report some of the warnings and errors we have
1622 * here. Down reports are critical, we leave them untouched.
1623 * Lack of report, or report of 'UP' leaves the room for
1624 * ERR first, then WARN.
1625 */
1626 const char *msg = cmd;
1627 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001628
Christopher Faulet61cc8522020-04-20 14:54:42 +02001629 if (!*msg || status == HCHK_STATUS_L7OKD) {
1630 if (err && *err)
1631 msg = err;
1632 else if (wrn && *wrn)
1633 msg = wrn;
1634 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001635
Christopher Faulet61cc8522020-04-20 14:54:42 +02001636 t = get_trash_chunk();
1637 chunk_printf(t, "via agent : %s%s%s%s",
1638 hs, *msg ? " (" : "",
1639 msg, *msg ? ")" : "");
1640 set_server_check_status(check, status, t->area);
1641 }
1642 else if (err && *err) {
1643 /* No status change but we'd like to report something odd.
1644 * Just report the current state and copy the message.
1645 */
1646 chunk_printf(&trash, "agent reports an error : %s", err);
1647 set_server_check_status(check, status/*check->status*/, trash.area);
1648 }
1649 else if (wrn && *wrn) {
1650 /* No status change but we'd like to report something odd.
1651 * Just report the current state and copy the message.
1652 */
1653 chunk_printf(&trash, "agent warns : %s", wrn);
1654 set_server_check_status(check, status/*check->status*/, trash.area);
1655 }
1656 else
1657 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001658
Christopher Faulet61cc8522020-04-20 14:54:42 +02001659 out:
1660 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001661
Christopher Faulet61cc8522020-04-20 14:54:42 +02001662 wait_more_data:
1663 ret = TCPCHK_EVAL_WAIT;
1664 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001665}
1666
Christopher Faulet61cc8522020-04-20 14:54:42 +02001667/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1668 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1669 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001670 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001671static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001672{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001673 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1674 struct tcpcheck_connect *connect = &rule->connect;
1675 struct proxy *proxy = check->proxy;
1676 struct server *s = check->server;
1677 struct task *t = check->task;
1678 struct conn_stream *cs;
1679 struct connection *conn = NULL;
1680 struct protocol *proto;
1681 struct xprt_ops *xprt;
1682 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001683
Christopher Faulet61cc8522020-04-20 14:54:42 +02001684 /* For a connect action we'll create a new connection. We may also have
1685 * to kill a previous one. But we don't want to leave *without* a
1686 * connection if we came here from the connection layer, hence with a
1687 * connection. Thus we'll proceed in the following order :
1688 * 1: close but not release previous connection (handled by the caller)
1689 * 2: try to get a new connection
1690 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001691 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001692
Christopher Faulet61cc8522020-04-20 14:54:42 +02001693 /* 2- prepare new connection */
1694 cs = cs_new(NULL);
1695 if (!cs) {
1696 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1697 tcpcheck_get_step_id(check, rule));
1698 if (rule->comment)
1699 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1700 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1701 ret = TCPCHK_EVAL_STOP;
1702 goto out;
1703 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001704
Christopher Faulet61cc8522020-04-20 14:54:42 +02001705 /* 3- release and replace the old one on success */
1706 if (check->cs) {
1707 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001708 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1709 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001710
1711 /* We may have been scheduled to run, and the I/O handler
1712 * expects to have a cs, so remove the tasklet
1713 */
1714 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1715 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001716 }
1717
Christopher Faulet61cc8522020-04-20 14:54:42 +02001718 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001719
Christopher Faulet61cc8522020-04-20 14:54:42 +02001720 check->cs = cs;
1721 conn = cs->conn;
1722 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001723
Christopher Faulet61cc8522020-04-20 14:54:42 +02001724 /* Maybe there were an older connection we were waiting on */
1725 check->wait_list.events = 0;
1726 conn->target = s ? &s->obj_type : &proxy->obj_type;
1727
1728 /* no client address */
1729 if (!sockaddr_alloc(&conn->dst)) {
1730 status = SF_ERR_RESOURCE;
1731 goto fail_check;
1732 }
1733
1734 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001735 * addr if specified on the server. otherwise, use the server addr (it
1736 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001737 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001738 *conn->dst = (is_addr(&connect->addr)
1739 ? connect->addr
1740 : (is_addr(&check->addr) ? check->addr : s->addr));
1741 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001742
Christopher Faulet61cc8522020-04-20 14:54:42 +02001743 port = 0;
1744 if (!port && connect->port)
1745 port = connect->port;
1746 if (!port && connect->port_expr) {
1747 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001748
Christopher Faulet61cc8522020-04-20 14:54:42 +02001749 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1750 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1751 connect->port_expr, SMP_T_SINT);
1752 if (smp)
1753 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001754 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001755 if (!port && is_inet_addr(&connect->addr))
1756 port = get_host_port(&connect->addr);
1757 if (!port && check->port)
1758 port = check->port;
1759 if (!port && is_inet_addr(&check->addr))
1760 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001761 if (!port) {
1762 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001763 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001764 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001765 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001766
Christopher Faulet61cc8522020-04-20 14:54:42 +02001767 xprt = ((connect->options & TCPCHK_OPT_SSL)
1768 ? xprt_get(XPRT_SSL)
1769 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001770
Christopher Faulet61cc8522020-04-20 14:54:42 +02001771 conn_prepare(conn, proto, xprt);
1772 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001773
Christopher Faulet61cc8522020-04-20 14:54:42 +02001774 status = SF_ERR_INTERNAL;
1775 if (proto && proto->connect) {
1776 struct tcpcheck_rule *next;
1777 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001778
Christopher Faulet61cc8522020-04-20 14:54:42 +02001779 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1780 flags |= CONNECT_HAS_DATA;
Christopher Faulet206368d2020-04-03 14:51:06 +02001781
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
1783 if (!next || next->action != TCPCHK_ACT_EXPECT)
1784 flags |= CONNECT_DELACK_ALWAYS;
1785 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001786 }
1787
Christopher Faulet61cc8522020-04-20 14:54:42 +02001788 if (status != SF_ERR_NONE)
1789 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001790
Christopher Faulet61cc8522020-04-20 14:54:42 +02001791 conn->flags |= CO_FL_PRIVATE;
1792 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001793
Christopher Faulet61cc8522020-04-20 14:54:42 +02001794 /* The mux may be initialized now if there isn't server attached to the
1795 * check (email alerts) or if there is a mux proto specified or if there
1796 * is no alpn.
1797 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001798 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1799 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001800 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001801
Christopher Faulet61cc8522020-04-20 14:54:42 +02001802 if (connect->mux_proto)
1803 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001804 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001805 mux_ops = check->mux_proto->mux;
1806 else {
1807 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1808 ? PROTO_MODE_HTTP
1809 : PROTO_MODE_TCP);
1810
1811 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001812 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001813 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1814 status = SF_ERR_INTERNAL;
1815 goto fail_check;
1816 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001817 }
1818
Christopher Faulet61cc8522020-04-20 14:54:42 +02001819#ifdef USE_OPENSSL
1820 if (connect->sni)
1821 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001822 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001823 ssl_sock_set_servername(conn, s->check.sni);
1824
1825 if (connect->alpn)
1826 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001827 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001828 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1829#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001830 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001831 conn->send_proxy_ofs = 1;
1832 conn->flags |= CO_FL_SOCKS4;
1833 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001834 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001835 conn->send_proxy_ofs = 1;
1836 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001837 }
1838
Christopher Faulet61cc8522020-04-20 14:54:42 +02001839 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1840 conn->send_proxy_ofs = 1;
1841 conn->flags |= CO_FL_SEND_PROXY;
1842 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001843 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001844 conn->send_proxy_ofs = 1;
1845 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001846 }
1847
Christopher Faulet61cc8522020-04-20 14:54:42 +02001848 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1849 /* Some servers don't like reset on close */
1850 fdtab[cs->conn->handle.fd].linger_risk = 0;
1851 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001852
Christopher Faulet61cc8522020-04-20 14:54:42 +02001853 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1854 if (xprt_add_hs(conn) < 0)
1855 status = SF_ERR_RESOURCE;
1856 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001857
Christopher Faulet61cc8522020-04-20 14:54:42 +02001858 fail_check:
1859 /* It can return one of :
1860 * - SF_ERR_NONE if everything's OK
1861 * - SF_ERR_SRVTO if there are no more servers
1862 * - SF_ERR_SRVCL if the connection was refused by the server
1863 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1864 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1865 * - SF_ERR_INTERNAL for any other purely internal errors
1866 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1867 * Note that we try to prevent the network stack from sending the ACK during the
1868 * connect() when a pure TCP check is used (without PROXY protocol).
1869 */
1870 switch (status) {
1871 case SF_ERR_NONE:
1872 /* we allow up to min(inter, timeout.connect) for a connection
1873 * to establish but only when timeout.check is set as it may be
1874 * to short for a full check otherwise
1875 */
1876 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001877
Christopher Faulet61cc8522020-04-20 14:54:42 +02001878 if (proxy->timeout.check && proxy->timeout.connect) {
1879 int t_con = tick_add(now_ms, proxy->timeout.connect);
1880 t->expire = tick_first(t->expire, t_con);
1881 }
1882 break;
1883 case SF_ERR_SRVTO: /* ETIMEDOUT */
1884 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1885 case SF_ERR_PRXCOND:
1886 case SF_ERR_RESOURCE:
1887 case SF_ERR_INTERNAL:
1888 chk_report_conn_err(check, errno, 0);
1889 ret = TCPCHK_EVAL_STOP;
1890 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001891 }
1892
Christopher Faulet61cc8522020-04-20 14:54:42 +02001893 /* don't do anything until the connection is established */
1894 if (conn->flags & CO_FL_WAIT_XPRT) {
1895 ret = TCPCHK_EVAL_WAIT;
1896 goto out;
1897 }
1898
1899 out:
1900 if (conn && check->result == CHK_RES_FAILED)
1901 conn->flags |= CO_FL_ERROR;
1902 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001903}
1904
Christopher Faulet61cc8522020-04-20 14:54:42 +02001905/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1906 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1907 * TCPCHK_EVAL_STOP if an error occurred.
1908 */
1909static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001910{
1911 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001912 struct tcpcheck_send *send = &rule->send;
1913 struct conn_stream *cs = check->cs;
1914 struct connection *conn = cs_conn(cs);
1915 struct buffer *tmp = NULL;
1916 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001917
Christopher Faulet61cc8522020-04-20 14:54:42 +02001918 /* reset the read & write buffer */
1919 b_reset(&check->bi);
1920 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001921
Christopher Faulet61cc8522020-04-20 14:54:42 +02001922 switch (send->type) {
1923 case TCPCHK_SEND_STRING:
1924 case TCPCHK_SEND_BINARY:
1925 if (istlen(send->data) >= b_size(&check->bo)) {
1926 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1927 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1928 tcpcheck_get_step_id(check, rule));
1929 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1930 ret = TCPCHK_EVAL_STOP;
1931 goto out;
1932 }
1933 b_putist(&check->bo, send->data);
1934 break;
1935 case TCPCHK_SEND_STRING_LF:
1936 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1937 if (!b_data(&check->bo))
1938 goto out;
1939 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001940 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001941 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001942
Christopher Faulet61cc8522020-04-20 14:54:42 +02001943 tmp = alloc_trash_chunk();
1944 if (!tmp)
1945 goto error_lf;
1946 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1947 if (!b_data(tmp))
1948 goto out;
1949 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001950 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001951 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001952 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001953 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001954 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001955 case TCPCHK_SEND_HTTP: {
1956 struct htx_sl *sl;
1957 struct ist meth, uri, vsn, clen, body;
1958 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001959
Christopher Faulet61cc8522020-04-20 14:54:42 +02001960 tmp = alloc_trash_chunk();
1961 if (!tmp)
1962 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001963
Christopher Faulet61cc8522020-04-20 14:54:42 +02001964 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1965 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1966 : http_known_methods[send->http.meth.meth]);
1967 uri = (isttest(send->http.uri) ? send->http.uri : ist("/")); // TODO: handle uri_fmt
1968 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001969
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001970 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1971 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001972 slflags |= HTX_SL_F_VER_11;
1973 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1974 if (!isttest(send->http.body))
1975 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001976
Christopher Faulet61cc8522020-04-20 14:54:42 +02001977 htx = htx_from_buf(&check->bo);
1978 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1979 if (!sl)
1980 goto error_htx;
1981 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001982 if (!http_update_host(htx, sl, uri))
1983 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001984
Christopher Faulet61cc8522020-04-20 14:54:42 +02001985 body = send->http.body; // TODO: handle body_fmt
1986 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001987
Christopher Faulet61cc8522020-04-20 14:54:42 +02001988 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
1989 !htx_add_header(htx, ist("Content-length"), clen))
1990 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001991
Christopher Faulet61cc8522020-04-20 14:54:42 +02001992 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1993 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001994 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001995
Christopher Faulet61cc8522020-04-20 14:54:42 +02001996 list_for_each_entry(hdr, &send->http.hdrs, list) {
1997 chunk_reset(tmp);
1998 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
1999 if (!b_data(tmp))
2000 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002001 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2002 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002003 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002004 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2005 if (!http_update_authority(htx, sl, hdr_value))
2006 goto error_htx;
2007 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002008 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002009
Christopher Faulet61cc8522020-04-20 14:54:42 +02002010 }
2011 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2012 chunk_reset(tmp);
2013 httpchk_build_status_header(check->server, tmp);
2014 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2015 goto error_htx;
2016 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002017
Christopher Faulet61cc8522020-04-20 14:54:42 +02002018 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
2019 (istlen(body) && !htx_add_data_atonce(htx, send->http.body)) ||
2020 !htx_add_endof(htx, HTX_BLK_EOM))
2021 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002022
Christopher Faulet61cc8522020-04-20 14:54:42 +02002023 htx_to_buf(htx, &check->bo);
2024 break;
2025 }
2026 case TCPCHK_SEND_UNDEF:
2027 /* Should never happen. */
2028 ret = TCPCHK_EVAL_STOP;
2029 goto out;
2030 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002031
Christopher Faulet6d471212020-04-22 11:09:25 +02002032
2033 if (conn->mux->snd_buf(cs, &check->bo,
2034 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002035 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002037 goto out;
2038 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002039 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002040 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002041 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2042 ret = TCPCHK_EVAL_WAIT;
2043 goto out;
2044 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002045
Christopher Faulet61cc8522020-04-20 14:54:42 +02002046 out:
2047 free_trash_chunk(tmp);
2048 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002049
Christopher Faulet61cc8522020-04-20 14:54:42 +02002050 error_htx:
2051 if (htx) {
2052 htx_reset(htx);
2053 htx_to_buf(htx, &check->bo);
2054 }
2055 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2056 tcpcheck_get_step_id(check, rule));
2057 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2058 ret = TCPCHK_EVAL_STOP;
2059 goto out;
2060
2061 error_lf:
2062 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2063 tcpcheck_get_step_id(check, rule));
2064 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2065 ret = TCPCHK_EVAL_STOP;
2066 goto out;
2067
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002068}
2069
Christopher Faulet61cc8522020-04-20 14:54:42 +02002070/* Try to reveice data before evaluting a tcp-check expect rule. Returns
2071 * TCPCHK_EVAL_WAIT if it is already subcribed on receive events or if nothing
2072 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2073 * TCPCHK_EVAL_STOP if an error occurred.
2074 */
2075static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002076{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002077 struct conn_stream *cs = check->cs;
2078 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002079 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002080 size_t max, read, cur_read = 0;
2081 int is_empty;
2082 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002083
Christopher Faulet61cc8522020-04-20 14:54:42 +02002084 if (check->wait_list.events & SUB_RETRY_RECV)
2085 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002086
Christopher Faulet61cc8522020-04-20 14:54:42 +02002087 if (cs->flags & CS_FL_EOS)
2088 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002089
Christopher Faulet61cc8522020-04-20 14:54:42 +02002090 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002091
Christopher Faulet61cc8522020-04-20 14:54:42 +02002092 /* prepare to detect if the mux needs more room */
2093 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002094
Christopher Faulet61cc8522020-04-20 14:54:42 +02002095 while ((cs->flags & CS_FL_RCV_MORE) ||
2096 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2097 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2098 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2099 cur_read += read;
2100 if (!read ||
2101 (cs->flags & CS_FL_WANT_ROOM) ||
2102 (--read_poll <= 0) ||
2103 (read < max && read >= global.tune.recv_enough))
2104 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002105 }
2106
Christopher Faulet61cc8522020-04-20 14:54:42 +02002107 end_recv:
2108 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2109 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2110 /* Report network errors only if we got no other data. Otherwise
2111 * we'll let the upper layers decide whether the response is OK
2112 * or not. It is very common that an RST sent by the server is
2113 * reported as an error just after the last data chunk.
2114 */
2115 goto stop;
2116 }
2117 if (!cur_read) {
2118 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2119 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2120 goto wait_more_data;
2121 }
2122 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002123 int status;
2124
Christopher Faulet61cc8522020-04-20 14:54:42 +02002125 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2126 tcpcheck_get_step_id(check, rule));
2127 if (rule->comment)
2128 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002129
2130 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2131 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002132 goto stop;
2133 }
2134 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002135
2136 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002137 return ret;
2138
Christopher Faulet61cc8522020-04-20 14:54:42 +02002139 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002140 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002141 goto out;
2142
2143 wait_more_data:
2144 ret = TCPCHK_EVAL_WAIT;
2145 goto out;
2146}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002147
Christopher Faulet61cc8522020-04-20 14:54:42 +02002148/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2149 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2150 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2151 * error occurred.
2152 */
2153static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, struct tcpcheck_rule *rule, int last_read)
Christopher Faulet267b01b2020-04-04 10:27:09 +02002154{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002155 struct htx *htx = htxbuf(&check->bi);
2156 struct htx_sl *sl;
2157 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002158 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002159 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002160 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002161 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002162 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002163 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002164
Christopher Faulet61cc8522020-04-20 14:54:42 +02002165 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002166
Christopher Faulet61cc8522020-04-20 14:54:42 +02002167 if (htx->flags & HTX_FL_PARSING_ERROR) {
2168 status = HCHK_STATUS_L7RSP;
2169 goto error;
2170 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002171
Christopher Faulet61cc8522020-04-20 14:54:42 +02002172 if (htx_is_empty(htx)) {
2173 if (last_read) {
2174 status = HCHK_STATUS_L7RSP;
2175 goto error;
2176 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002177 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002178 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002179
Christopher Faulet61cc8522020-04-20 14:54:42 +02002180 sl = http_get_stline(htx);
2181 check->code = sl->info.res.status;
2182
2183 if (check->server &&
2184 (check->server->proxy->options & PR_O_DISABLE404) &&
2185 (check->server->next_state != SRV_ST_STOPPED) &&
2186 (check->code == 404)) {
2187 /* 404 may be accepted as "stopping" only if the server was up */
2188 goto out;
2189 }
2190
2191 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2192 /* Make GCC happy ; initialize match to a failure state. */
2193 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002194 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002195
2196 switch (expect->type) {
2197 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002198 match = 0;
2199 for (i = 0; i < expect->codes.num; i++) {
2200 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2201 sl->info.res.status <= expect->codes.codes[i][1]) {
2202 match = 1;
2203 break;
2204 }
2205 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002206
2207 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002208 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002209 if (LIST_ISEMPTY(&expect->onerror_fmt))
2210 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002211 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002212 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002213 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2214
2215 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002216 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002217 if (LIST_ISEMPTY(&expect->onerror_fmt))
2218 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002219 break;
2220
Christopher Faulet39708192020-05-05 10:47:36 +02002221 case TCPCHK_EXPECT_HTTP_HEADER: {
2222 struct http_hdr_ctx ctx;
2223 struct ist npat, vpat, value;
2224 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2225
2226 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2227 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002228 if (!nbuf) {
2229 status = HCHK_STATUS_L7RSP;
2230 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002231 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002232 }
Christopher Faulet39708192020-05-05 10:47:36 +02002233 nbuf->data = sess_build_logline(check->sess, NULL, b_orig(nbuf), b_size(nbuf), &expect->hdr.name_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002234 if (!b_data(nbuf)) {
2235 status = HCHK_STATUS_L7RSP;
2236 desc = ist("log-format string evaluated to an empty string");
2237 goto error;
2238 }
Christopher Faulet39708192020-05-05 10:47:36 +02002239 npat = ist2(b_orig(nbuf), b_data(nbuf));
2240 }
2241 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2242 npat = expect->hdr.name;
2243
2244 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2245 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002246 if (!vbuf) {
2247 status = HCHK_STATUS_L7RSP;
2248 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002249 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002250 }
Christopher Faulet39708192020-05-05 10:47:36 +02002251 vbuf->data = sess_build_logline(check->sess, NULL, b_orig(vbuf), b_size(vbuf), &expect->hdr.value_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002252 if (!b_data(vbuf)) {
2253 status = HCHK_STATUS_L7RSP;
2254 desc = ist("log-format string evaluated to an empty string");
2255 goto error;
2256 }
Christopher Faulet39708192020-05-05 10:47:36 +02002257 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2258 }
2259 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2260 vpat = expect->hdr.value;
2261
2262 match = 0;
2263 ctx.blk = NULL;
2264 while (1) {
2265 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2266 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2267 if (!http_find_str_header(htx, npat, &ctx, full))
2268 goto end_of_match;
2269 break;
2270 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2271 if (!http_find_pfx_header(htx, npat, &ctx, full))
2272 goto end_of_match;
2273 break;
2274 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2275 if (!http_find_sfx_header(htx, npat, &ctx, full))
2276 goto end_of_match;
2277 break;
2278 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2279 if (!http_find_sub_header(htx, npat, &ctx, full))
2280 goto end_of_match;
2281 break;
2282 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2283 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2284 goto end_of_match;
2285 break;
2286 }
2287
2288 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2289 match = 1;
2290 goto end_of_match;
2291 }
2292
2293 value = ctx.value;
2294 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2295 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2296 if (isteq(value, vpat)) {
2297 match = 1;
2298 goto end_of_match;
2299 }
2300 break;
2301 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2302 if (istlen(value) < istlen(vpat))
2303 break;
2304 value = ist2(istptr(value), istlen(vpat));
2305 if (isteq(value, vpat)) {
2306 match = 1;
2307 goto end_of_match;
2308 }
2309 break;
2310 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2311 if (istlen(value) < istlen(vpat))
2312 break;
2313 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2314 if (isteq(value, vpat)) {
2315 match = 1;
2316 goto end_of_match;
2317 }
2318 break;
2319 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2320 if (isttest(istist(value, vpat))) {
2321 match = 1;
2322 goto end_of_match;
2323 }
2324 break;
2325 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2326 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2327 match = 1;
2328 goto end_of_match;
2329 }
2330 break;
2331 }
2332 }
2333
2334 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002335 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002336 if (LIST_ISEMPTY(&expect->onerror_fmt))
2337 desc = htx_sl_res_reason(sl);
2338 break;
2339 }
2340
Christopher Faulet61cc8522020-04-20 14:54:42 +02002341 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002342 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002343 case TCPCHK_EXPECT_HTTP_BODY_LF:
2344 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002345 chunk_reset(&trash);
2346 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2347 enum htx_blk_type type = htx_get_blk_type(blk);
2348
2349 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2350 break;
2351 if (type == HTX_BLK_DATA) {
2352 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2353 break;
2354 }
2355 }
2356
2357 if (!b_data(&trash)) {
2358 if (!last_read)
2359 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002360 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002361 if (LIST_ISEMPTY(&expect->onerror_fmt))
2362 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002363 goto error;
2364 }
2365
Christopher Fauletaaab0832020-05-05 15:54:22 +02002366 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2367 tmp = alloc_trash_chunk();
2368 if (!tmp) {
2369 status = HCHK_STATUS_L7RSP;
2370 desc = ist("Failed to allocate buffer to eval log-format string");
2371 goto error;
2372 }
2373 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2374 if (!b_data(tmp)) {
2375 status = HCHK_STATUS_L7RSP;
2376 desc = ist("log-format string evaluated to an empty string");
2377 goto error;
2378 }
2379 }
2380
Christopher Faulet61cc8522020-04-20 14:54:42 +02002381 if (!last_read &&
2382 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002383 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002384 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2385 ret = TCPCHK_EVAL_WAIT;
2386 goto out;
2387 }
2388
2389 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002390 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002391 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2392 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002393 else
2394 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2395
2396 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002397 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002398 if (LIST_ISEMPTY(&expect->onerror_fmt))
2399 desc = (inverse
2400 ? ist("HTTP check matched unwanted content")
2401 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002402 break;
2403
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002404
Christopher Faulet61cc8522020-04-20 14:54:42 +02002405 default:
2406 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002407 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002408 goto error;
2409 }
2410
Christopher Faulet61cc8522020-04-20 14:54:42 +02002411 /* Wait for more data on mismatch only if no minimum is defined (-1),
2412 * otherwise the absence of match is already conclusive.
2413 */
2414 if (!match && !last_read && (expect->min_recv == -1)) {
2415 ret = TCPCHK_EVAL_WAIT;
2416 goto out;
2417 }
2418
2419 if (!(match ^ inverse))
2420 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002421
2422 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002423 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002424 free_trash_chunk(nbuf);
2425 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002426 free_trash_chunk(msg);
2427 return ret;
2428
2429 error:
2430 ret = TCPCHK_EVAL_STOP;
2431 msg = alloc_trash_chunk();
2432 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002433 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002434 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2435 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002436
2437 wait_more_data:
2438 ret = TCPCHK_EVAL_WAIT;
2439 goto out;
2440}
2441
Christopher Faulet61cc8522020-04-20 14:54:42 +02002442/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2443 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2444 * if an error occurred.
2445 */
2446static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2447{
2448 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2449 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002450 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002451 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002452 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002453 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002454
Christopher Faulet61cc8522020-04-20 14:54:42 +02002455 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002456
Christopher Faulet61cc8522020-04-20 14:54:42 +02002457 /* The current expect might need more data than the previous one, check again
2458 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002459 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002460 if (!last_read) {
2461 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2462 (b_data(&check->bi) < istlen(expect->data))) {
2463 ret = TCPCHK_EVAL_WAIT;
2464 goto out;
2465 }
2466 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2467 ret = TCPCHK_EVAL_WAIT;
2468 goto out;
2469 }
2470 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002471
Christopher Faulet61cc8522020-04-20 14:54:42 +02002472 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2473 /* Make GCC happy ; initialize match to a failure state. */
2474 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002475 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002476
Christopher Faulet61cc8522020-04-20 14:54:42 +02002477 switch (expect->type) {
2478 case TCPCHK_EXPECT_STRING:
2479 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002480 match = my_memmem(b_head(&check->bi), b_data(&check->bi), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002482 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002483 match = regex_exec2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002484 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002485
Christopher Faulet67a23452020-05-05 18:10:01 +02002486 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002487 chunk_reset(&trash);
2488 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002489 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002490 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002491
2492 case TCPCHK_EXPECT_STRING_LF:
2493 case TCPCHK_EXPECT_BINARY_LF:
2494 match = 0;
2495 tmp = alloc_trash_chunk();
2496 if (!tmp) {
2497 status = HCHK_STATUS_L7RSP;
2498 desc = ist("Failed to allocate buffer to eval format string");
2499 goto error;
2500 }
2501 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2502 if (!b_data(tmp)) {
2503 status = HCHK_STATUS_L7RSP;
2504 desc = ist("log-format string evaluated to an empty string");
2505 goto error;
2506 }
2507 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2508 int len = tmp->data;
2509 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2510 status = HCHK_STATUS_L7RSP;
2511 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2512 goto error;
2513 }
2514 tmp->data = len;
2515 }
2516 if (b_data(&check->bi) < tmp->data) {
2517 if (!last_read) {
2518 ret = TCPCHK_EVAL_WAIT;
2519 goto out;
2520 }
2521 break;
2522 }
2523 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2524 break;
2525
Christopher Faulet61cc8522020-04-20 14:54:42 +02002526 case TCPCHK_EXPECT_CUSTOM:
2527 if (expect->custom)
2528 ret = expect->custom(check, rule, last_read);
2529 goto out;
2530 default:
2531 /* Should never happen. */
2532 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002533 goto out;
2534 }
2535
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002536
Christopher Faulet61cc8522020-04-20 14:54:42 +02002537 /* Wait for more data on mismatch only if no minimum is defined (-1),
2538 * otherwise the absence of match is already conclusive.
2539 */
2540 if (!match && !last_read && (expect->min_recv == -1)) {
2541 ret = TCPCHK_EVAL_WAIT;
2542 goto out;
2543 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002544
Christopher Faulet61cc8522020-04-20 14:54:42 +02002545 /* Result as expected, next rule. */
2546 if (match ^ inverse)
2547 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002548
Christopher Fauletaaab0832020-05-05 15:54:22 +02002549 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002550 /* From this point on, we matched something we did not want, this is an error state. */
2551 ret = TCPCHK_EVAL_STOP;
2552 msg = alloc_trash_chunk();
2553 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002554 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002555 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002556 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002557
Christopher Faulet61cc8522020-04-20 14:54:42 +02002558 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002559 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002560 return ret;
2561}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002562
Christopher Faulet61cc8522020-04-20 14:54:42 +02002563/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
2564 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It nevers
2565 * waits.
2566 */
2567static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2568{
2569 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2570 struct act_rule *act_rule;
2571 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002572
Christopher Faulet61cc8522020-04-20 14:54:42 +02002573 act_rule =rule->action_kw.rule;
2574 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2575 if (act_ret != ACT_RET_CONT) {
2576 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2577 tcpcheck_get_step_id(check, rule));
2578 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2579 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002580 }
2581
Christopher Faulet61cc8522020-04-20 14:54:42 +02002582 return ret;
2583}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002584
Christopher Faulet61cc8522020-04-20 14:54:42 +02002585/* Executes a tcp-check ruleset. Note that this is called both from the
2586 * connection's wake() callback and from the check scheduling task. It returns
2587 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2588 * presenting the risk of an fd replacement.
2589 *
2590 * Please do NOT place any return statement in this function and only leave
2591 * via the out_end_tcpcheck label after setting retcode.
2592 */
2593static int tcpcheck_main(struct check *check)
2594{
2595 struct tcpcheck_rule *rule;
2596 struct conn_stream *cs = check->cs;
2597 struct connection *conn = cs_conn(cs);
2598 int must_read = 1, last_read = 0;
2599 int ret, retcode = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002600
Christopher Faulet61cc8522020-04-20 14:54:42 +02002601 /* here, we know that the check is complete or that it failed */
2602 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002603 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002604
Christopher Faulet61cc8522020-04-20 14:54:42 +02002605 /* 1- check for connection error, if any */
2606 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2607 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002608
Christopher Faulet61cc8522020-04-20 14:54:42 +02002609 /* 2- check if we are waiting for the connection establishment. It only
2610 * happens during TCPCHK_ACT_CONNECT. */
2611 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
2612 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
2613 if (conn && (conn->flags & CO_FL_WAIT_XPRT)) {
2614 if (rule->action == TCPCHK_ACT_SEND)
2615 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2616 else if (rule->action == TCPCHK_ACT_EXPECT)
2617 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2618 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002619 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002620 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002621
2622 /* 3- check for pending outgoing data. It only happens during
2623 * TCPCHK_ACT_SEND. */
2624 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
2625 if (conn && b_data(&check->bo)) {
Christopher Faulet6d471212020-04-22 11:09:25 +02002626 ret = conn->mux->snd_buf(cs, &check->bo,
2627 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002628 if (ret <= 0) {
2629 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2630 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002631 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002632 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002633 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2634 goto out;
2635 }
2636 }
2637 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002638 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002639
Christopher Faulet61cc8522020-04-20 14:54:42 +02002640 /* 4- check if a rule must be resume. It happens if check->current_step
2641 * is defined. */
2642 else if (check->current_step)
2643 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002644
Christopher Faulet61cc8522020-04-20 14:54:42 +02002645 /* 5- It is the first evaluation. We must create a session and preset
2646 * tcp-check variables */
2647 else {
2648 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002649
Christopher Faulet61cc8522020-04-20 14:54:42 +02002650 /* First evaluation, create a session */
2651 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2652 if (!check->sess) {
2653 chunk_printf(&trash, "TCPCHK error allocating check session");
2654 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2655 goto out_end_tcpcheck;
2656 }
2657 vars_init(&check->vars, SCOPE_CHECK);
2658 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002659
Christopher Faulet61cc8522020-04-20 14:54:42 +02002660 /* Preset tcp-check variables */
2661 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2662 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002663
Christopher Faulet61cc8522020-04-20 14:54:42 +02002664 memset(&smp, 0, sizeof(smp));
2665 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2666 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002667 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002668 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002669 }
2670
Christopher Faulet61cc8522020-04-20 14:54:42 +02002671 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002672
Christopher Faulet61cc8522020-04-20 14:54:42 +02002673 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
2674 enum tcpcheck_eval_ret eval_ret;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002675
Christopher Faulet61cc8522020-04-20 14:54:42 +02002676 check->code = 0;
2677 switch (rule->action) {
2678 case TCPCHK_ACT_CONNECT:
2679 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002680
Christopher Faulet61cc8522020-04-20 14:54:42 +02002681 /* close but not release yet previous connection */
2682 if (check->cs) {
2683 cs_close(check->cs);
2684 retcode = -1; /* do not reuse the fd in the caller! */
2685 }
2686 eval_ret = tcpcheck_eval_connect(check, rule);
2687 must_read = 1; last_read = 0;
2688 break;
2689 case TCPCHK_ACT_SEND:
2690 check->current_step = rule;
2691 eval_ret = tcpcheck_eval_send(check, rule);
2692 must_read = 1;
2693 break;
2694 case TCPCHK_ACT_EXPECT:
2695 check->current_step = rule;
2696 if (must_read) {
2697 if (check->proxy->timeout.check)
2698 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002699
Christopher Faulet61cc8522020-04-20 14:54:42 +02002700 eval_ret = tcpcheck_eval_recv(check, rule);
2701 if (eval_ret == TCPCHK_EVAL_STOP)
2702 goto out_end_tcpcheck;
2703 else if (eval_ret == TCPCHK_EVAL_WAIT)
2704 goto out;
2705 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2706 must_read = 0;
2707 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002708
Christopher Faulet61cc8522020-04-20 14:54:42 +02002709 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2710 ? tcpcheck_eval_expect_http(check, rule, last_read)
2711 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002712
Christopher Faulet61cc8522020-04-20 14:54:42 +02002713 if (eval_ret == TCPCHK_EVAL_WAIT) {
2714 check->current_step = rule->expect.head;
2715 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2716 }
2717 break;
2718 case TCPCHK_ACT_ACTION_KW:
2719 /* Don't update the current step */
2720 eval_ret = tcpcheck_eval_action_kw(check, rule);
2721 break;
2722 default:
2723 /* Otherwise, just go to the next one and don't update
2724 * the current step
2725 */
2726 eval_ret = TCPCHK_EVAL_CONTINUE;
2727 break;
2728 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002729
Christopher Faulet61cc8522020-04-20 14:54:42 +02002730 switch (eval_ret) {
2731 case TCPCHK_EVAL_CONTINUE:
2732 break;
2733 case TCPCHK_EVAL_WAIT:
2734 goto out;
2735 case TCPCHK_EVAL_STOP:
2736 goto out_end_tcpcheck;
2737 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002738 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002739
Christopher Faulet61cc8522020-04-20 14:54:42 +02002740 /* All rules was evaluated */
2741 if (check->current_step) {
2742 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002743
Christopher Faulet61cc8522020-04-20 14:54:42 +02002744 if (rule->action == TCPCHK_ACT_EXPECT) {
2745 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002746 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002747
Christopher Faulet61cc8522020-04-20 14:54:42 +02002748 if (check->server &&
2749 (check->server->proxy->options & PR_O_DISABLE404) &&
2750 (check->server->next_state != SRV_ST_STOPPED) &&
2751 (check->code == 404)) {
2752 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2753 goto out_end_tcpcheck;
2754 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002755
Christopher Faulet61cc8522020-04-20 14:54:42 +02002756 msg = alloc_trash_chunk();
2757 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002758 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002759 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2760 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002761 free_trash_chunk(msg);
2762 }
2763 else if (rule->action == TCPCHK_ACT_CONNECT) {
2764 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002765 enum healthcheck_status status = HCHK_STATUS_L4OK;
2766#ifdef USE_OPENSSL
2767 if (conn && ssl_sock_is_ssl(conn))
2768 status = HCHK_STATUS_L6OK;
2769#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002770 set_server_check_status(check, status, msg);
2771 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002772 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002773 else
2774 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002775
Christopher Faulet61cc8522020-04-20 14:54:42 +02002776 out_end_tcpcheck:
2777 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2778 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002779
Christopher Faulet61cc8522020-04-20 14:54:42 +02002780 out:
2781 return retcode;
2782}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002783
Christopher Faulet14cd3162020-04-16 14:50:06 +02002784
Christopher Faulet61cc8522020-04-20 14:54:42 +02002785/**************************************************************************/
2786/************** Health-checks based on an external process ****************/
2787/**************************************************************************/
2788static struct list pid_list = LIST_HEAD_INIT(pid_list);
2789static struct pool_head *pool_head_pid_list;
2790__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002791
Christopher Faulet61cc8522020-04-20 14:54:42 +02002792struct extcheck_env {
2793 char *name; /* environment variable name */
2794 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2795};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002796
Christopher Faulet61cc8522020-04-20 14:54:42 +02002797/* environment variables memory requirement for different types of data */
2798#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2799 * such environment variables are not updatable. */
2800#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2801#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2802#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002803
Christopher Faulet61cc8522020-04-20 14:54:42 +02002804/* external checks environment variables */
2805enum {
2806 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002807
Christopher Faulet61cc8522020-04-20 14:54:42 +02002808 /* Proxy specific environment variables */
2809 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2810 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2811 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2812 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002813
Christopher Faulet61cc8522020-04-20 14:54:42 +02002814 /* Server specific environment variables */
2815 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2816 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2817 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2818 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2819 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2820 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002821
Christopher Faulet61cc8522020-04-20 14:54:42 +02002822 EXTCHK_SIZE
2823};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002824
Christopher Faulet61cc8522020-04-20 14:54:42 +02002825const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2826 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2827 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2828 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2829 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2830 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2831 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2832 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2833 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2834 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2835 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2836 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2837};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002838
Christopher Faulet61cc8522020-04-20 14:54:42 +02002839void block_sigchld(void)
2840{
2841 sigset_t set;
2842 sigemptyset(&set);
2843 sigaddset(&set, SIGCHLD);
2844 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2845}
Willy Tarreaube373152018-09-06 11:45:30 +02002846
Christopher Faulet61cc8522020-04-20 14:54:42 +02002847void unblock_sigchld(void)
2848{
2849 sigset_t set;
2850 sigemptyset(&set);
2851 sigaddset(&set, SIGCHLD);
2852 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002853}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002854
Christopher Faulet61cc8522020-04-20 14:54:42 +02002855static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002856{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002857 struct pid_list *elem;
2858 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002859
Christopher Faulet61cc8522020-04-20 14:54:42 +02002860 elem = pool_alloc(pool_head_pid_list);
2861 if (!elem)
2862 return NULL;
2863 elem->pid = pid;
2864 elem->t = t;
2865 elem->exited = 0;
2866 check->curpid = elem;
2867 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002868
Christopher Faulet61cc8522020-04-20 14:54:42 +02002869 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2870 LIST_ADD(&pid_list, &elem->list);
2871 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002872
Christopher Faulet61cc8522020-04-20 14:54:42 +02002873 return elem;
2874}
Christopher Faulete5870d82020-04-15 11:32:03 +02002875
Christopher Faulet61cc8522020-04-20 14:54:42 +02002876static void pid_list_del(struct pid_list *elem)
2877{
2878 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002879
Christopher Faulet61cc8522020-04-20 14:54:42 +02002880 if (!elem)
2881 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002882
Christopher Faulet61cc8522020-04-20 14:54:42 +02002883 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2884 LIST_DEL(&elem->list);
2885 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002886
Christopher Faulet61cc8522020-04-20 14:54:42 +02002887 if (!elem->exited)
2888 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002889
Christopher Faulet61cc8522020-04-20 14:54:42 +02002890 check = elem->t->context;
2891 check->curpid = NULL;
2892 pool_free(pool_head_pid_list, elem);
2893}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002894
Christopher Faulet61cc8522020-04-20 14:54:42 +02002895/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2896static void pid_list_expire(pid_t pid, int status)
2897{
2898 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002899
Christopher Faulet61cc8522020-04-20 14:54:42 +02002900 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2901 list_for_each_entry(elem, &pid_list, list) {
2902 if (elem->pid == pid) {
2903 elem->t->expire = now_ms;
2904 elem->status = status;
2905 elem->exited = 1;
2906 task_wakeup(elem->t, TASK_WOKEN_IO);
2907 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002908 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002909 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002910 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2911}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002912
Christopher Faulet61cc8522020-04-20 14:54:42 +02002913static void sigchld_handler(struct sig_handler *sh)
2914{
2915 pid_t pid;
2916 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002917
Christopher Faulet61cc8522020-04-20 14:54:42 +02002918 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2919 pid_list_expire(pid, status);
2920}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002921
Christopher Faulet61cc8522020-04-20 14:54:42 +02002922static int init_pid_list(void)
2923{
2924 if (pool_head_pid_list != NULL)
2925 /* Nothing to do */
2926 return 0;
2927
2928 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2929 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2930 strerror(errno));
2931 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002932 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002933
Christopher Faulet61cc8522020-04-20 14:54:42 +02002934 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2935 if (pool_head_pid_list == NULL) {
2936 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2937 strerror(errno));
2938 return 1;
2939 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002940
Christopher Faulet61cc8522020-04-20 14:54:42 +02002941 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002942}
2943
Christopher Faulet61cc8522020-04-20 14:54:42 +02002944/* helper macro to set an environment variable and jump to a specific label on failure. */
2945#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002946
Christopher Faulet61cc8522020-04-20 14:54:42 +02002947/*
2948 * helper function to allocate enough memory to store an environment variable.
2949 * It will also check that the environment variable is updatable, and silently
2950 * fail if not.
2951 */
2952static int extchk_setenv(struct check *check, int idx, const char *value)
2953{
2954 int len, ret;
2955 char *envname;
2956 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002957
Christopher Faulet61cc8522020-04-20 14:54:42 +02002958 if (idx < 0 || idx >= EXTCHK_SIZE) {
2959 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
2960 return 1;
2961 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002962
Christopher Faulet61cc8522020-04-20 14:54:42 +02002963 envname = extcheck_envs[idx].name;
2964 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002965
Christopher Faulet61cc8522020-04-20 14:54:42 +02002966 /* Check if the environment variable is already set, and silently reject
2967 * the update if this one is not updatable. */
2968 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
2969 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002970
Christopher Faulet61cc8522020-04-20 14:54:42 +02002971 /* Instead of sending NOT_USED, sending an empty value is preferable */
2972 if (strcmp(value, "NOT_USED") == 0) {
2973 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02002974 }
2975
Christopher Faulet61cc8522020-04-20 14:54:42 +02002976 len = strlen(envname) + 1;
2977 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
2978 len += strlen(value);
2979 else
2980 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002981
Christopher Faulet61cc8522020-04-20 14:54:42 +02002982 if (!check->envp[idx])
2983 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02002984
Christopher Faulet61cc8522020-04-20 14:54:42 +02002985 if (!check->envp[idx]) {
2986 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
2987 return 1;
2988 }
2989 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
2990 if (ret < 0) {
2991 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
2992 return 1;
2993 }
2994 else if (ret > len) {
2995 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
2996 return 1;
2997 }
2998 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002999}
3000
Christopher Faulet61cc8522020-04-20 14:54:42 +02003001static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003002{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003003 struct server *s = check->server;
3004 struct proxy *px = s->proxy;
3005 struct listener *listener = NULL, *l;
3006 int i;
3007 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3008 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003009
Christopher Faulet61cc8522020-04-20 14:54:42 +02003010 list_for_each_entry(l, &px->conf.listeners, by_fe)
3011 /* Use the first INET, INET6 or UNIX listener */
3012 if (l->addr.ss_family == AF_INET ||
3013 l->addr.ss_family == AF_INET6 ||
3014 l->addr.ss_family == AF_UNIX) {
3015 listener = l;
3016 break;
3017 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003018
Christopher Faulet61cc8522020-04-20 14:54:42 +02003019 check->curpid = NULL;
3020 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3021 if (!check->envp) {
3022 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3023 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003024 }
3025
Christopher Faulet61cc8522020-04-20 14:54:42 +02003026 check->argv = calloc(6, sizeof(char *));
3027 if (!check->argv) {
3028 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3029 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003030 }
3031
Christopher Faulet61cc8522020-04-20 14:54:42 +02003032 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003033
Christopher Faulet61cc8522020-04-20 14:54:42 +02003034 if (!listener) {
3035 check->argv[1] = strdup("NOT_USED");
3036 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003037 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003038 else if (listener->addr.ss_family == AF_INET ||
3039 listener->addr.ss_family == AF_INET6) {
3040 addr_to_str(&listener->addr, buf, sizeof(buf));
3041 check->argv[1] = strdup(buf);
3042 port_to_str(&listener->addr, buf, sizeof(buf));
3043 check->argv[2] = strdup(buf);
3044 }
3045 else if (listener->addr.ss_family == AF_UNIX) {
3046 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003047
Christopher Faulet61cc8522020-04-20 14:54:42 +02003048 un = (struct sockaddr_un *)&listener->addr;
3049 check->argv[1] = strdup(un->sun_path);
3050 check->argv[2] = strdup("NOT_USED");
3051 }
3052 else {
3053 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3054 goto err;
3055 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003056
Christopher Faulet61cc8522020-04-20 14:54:42 +02003057 if (!check->argv[1] || !check->argv[2]) {
3058 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3059 goto err;
3060 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003061
Christopher Faulet61cc8522020-04-20 14:54:42 +02003062 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3063 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3064 if (!check->argv[3] || !check->argv[4]) {
3065 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3066 goto err;
3067 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003068
Christopher Faulet61cc8522020-04-20 14:54:42 +02003069 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3070 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3071 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003072
Christopher Faulet61cc8522020-04-20 14:54:42 +02003073 for (i = 0; i < 5; i++) {
3074 if (!check->argv[i]) {
3075 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3076 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003077 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003078 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003079
Christopher Faulet61cc8522020-04-20 14:54:42 +02003080 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3081 /* Add proxy environment variables */
3082 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3083 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3084 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3085 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3086 /* Add server environment variables */
3087 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3088 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3089 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3090 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3091 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3092 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003093
Christopher Faulet61cc8522020-04-20 14:54:42 +02003094 /* Ensure that we don't leave any hole in check->envp */
3095 for (i = 0; i < EXTCHK_SIZE; i++)
3096 if (!check->envp[i])
3097 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003098
Christopher Faulet61cc8522020-04-20 14:54:42 +02003099 return 1;
3100err:
3101 if (check->envp) {
3102 for (i = 0; i < EXTCHK_SIZE; i++)
3103 free(check->envp[i]);
3104 free(check->envp);
3105 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003106 }
3107
Christopher Faulet61cc8522020-04-20 14:54:42 +02003108 if (check->argv) {
3109 for (i = 1; i < 5; i++)
3110 free(check->argv[i]);
3111 free(check->argv);
3112 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003113 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003114 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003115}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003116
Christopher Faulet61cc8522020-04-20 14:54:42 +02003117/*
3118 * establish a server health-check that makes use of a process.
3119 *
3120 * It can return one of :
3121 * - SF_ERR_NONE if everything's OK
3122 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3123 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3124 *
3125 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003126 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003127static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003128{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003129 char buf[256];
3130 struct check *check = t->context;
3131 struct server *s = check->server;
3132 struct proxy *px = s->proxy;
3133 int status;
3134 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003135
Christopher Faulet61cc8522020-04-20 14:54:42 +02003136 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003137
Christopher Faulet61cc8522020-04-20 14:54:42 +02003138 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003139
Christopher Faulet61cc8522020-04-20 14:54:42 +02003140 pid = fork();
3141 if (pid < 0) {
3142 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3143 (global.tune.options & GTUNE_INSECURE_FORK) ?
3144 "" : " (likely caused by missing 'insecure-fork-wanted')",
3145 strerror(errno));
3146 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003147 goto out;
3148 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003149 if (pid == 0) {
3150 /* Child */
3151 extern char **environ;
3152 struct rlimit limit;
3153 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003154
Christopher Faulet61cc8522020-04-20 14:54:42 +02003155 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3156 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003157
Christopher Faulet61cc8522020-04-20 14:54:42 +02003158 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003159
Christopher Faulet61cc8522020-04-20 14:54:42 +02003160 /* restore the initial FD limits */
3161 limit.rlim_cur = rlim_fd_cur_at_boot;
3162 limit.rlim_max = rlim_fd_max_at_boot;
3163 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3164 getrlimit(RLIMIT_NOFILE, &limit);
3165 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3166 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3167 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3168 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003169
Christopher Faulet61cc8522020-04-20 14:54:42 +02003170 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003171
Christopher Faulet61cc8522020-04-20 14:54:42 +02003172 /* Update some environment variables and command args: curconn, server addr and server port */
3173 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003174
Christopher Faulet61cc8522020-04-20 14:54:42 +02003175 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3176 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003177
Christopher Faulet61cc8522020-04-20 14:54:42 +02003178 *check->argv[4] = 0;
3179 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3180 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3181 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003182
Christopher Faulet61cc8522020-04-20 14:54:42 +02003183 haproxy_unblock_signals();
3184 execvp(px->check_command, check->argv);
3185 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3186 strerror(errno));
3187 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003188 }
3189
Christopher Faulet61cc8522020-04-20 14:54:42 +02003190 /* Parent */
3191 if (check->result == CHK_RES_UNKNOWN) {
3192 if (pid_list_add(pid, t) != NULL) {
3193 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3194
3195 if (px->timeout.check && px->timeout.connect) {
3196 int t_con = tick_add(now_ms, px->timeout.connect);
3197 t->expire = tick_first(t->expire, t_con);
3198 }
3199 status = SF_ERR_NONE;
3200 goto out;
3201 }
3202 else {
3203 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3204 }
3205 kill(pid, SIGTERM); /* process creation error */
3206 }
3207 else
3208 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3209
3210out:
3211 unblock_sigchld();
3212 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003213}
3214
Christopher Faulet61cc8522020-04-20 14:54:42 +02003215/*
3216 * manages a server health-check that uses an external process. Returns
3217 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003218 *
3219 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003220 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003221 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003222static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003223{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003224 struct check *check = context;
3225 struct server *s = check->server;
3226 int rv;
3227 int ret;
3228 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003229
Christopher Faulet61cc8522020-04-20 14:54:42 +02003230 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3231 if (!(check->state & CHK_ST_INPROGRESS)) {
3232 /* no check currently running */
3233 if (!expired) /* woke up too early */
3234 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003235
Christopher Faulet61cc8522020-04-20 14:54:42 +02003236 /* we don't send any health-checks when the proxy is
3237 * stopped, the server should not be checked or the check
3238 * is disabled.
3239 */
3240 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3241 s->proxy->state == PR_STSTOPPED)
3242 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003243
Christopher Faulet61cc8522020-04-20 14:54:42 +02003244 /* we'll initiate a new check */
3245 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003246
Christopher Faulet61cc8522020-04-20 14:54:42 +02003247 check->state |= CHK_ST_INPROGRESS;
3248
3249 ret = connect_proc_chk(t);
3250 if (ret == SF_ERR_NONE) {
3251 /* the process was forked, we allow up to min(inter,
3252 * timeout.connect) for it to report its status, but
3253 * only when timeout.check is set as it may be to short
3254 * for a full check otherwise.
3255 */
3256 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3257
3258 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3259 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3260 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003261 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003262 task_set_affinity(t, tid_bit);
3263 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003264 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003265
Christopher Faulet61cc8522020-04-20 14:54:42 +02003266 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003267
Christopher Faulet61cc8522020-04-20 14:54:42 +02003268 check->state &= ~CHK_ST_INPROGRESS;
3269 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003270
Christopher Faulet61cc8522020-04-20 14:54:42 +02003271 /* we allow up to min(inter, timeout.connect) for a connection
3272 * to establish but only when timeout.check is set
3273 * as it may be to short for a full check otherwise
3274 */
3275 while (tick_is_expired(t->expire, now_ms)) {
3276 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003277
Christopher Faulet61cc8522020-04-20 14:54:42 +02003278 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3279 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003280
Christopher Faulet61cc8522020-04-20 14:54:42 +02003281 if (s->proxy->timeout.check)
3282 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003283 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003284 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003285 else {
3286 /* there was a test running.
3287 * First, let's check whether there was an uncaught error,
3288 * which can happen on connect timeout or error.
3289 */
3290 if (check->result == CHK_RES_UNKNOWN) {
3291 /* good connection is enough for pure TCP check */
3292 struct pid_list *elem = check->curpid;
3293 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003294
Christopher Faulet61cc8522020-04-20 14:54:42 +02003295 if (elem->exited) {
3296 status = elem->status; /* Save in case the process exits between use below */
3297 if (!WIFEXITED(status))
3298 check->code = -1;
3299 else
3300 check->code = WEXITSTATUS(status);
3301 if (!WIFEXITED(status) || WEXITSTATUS(status))
3302 status = HCHK_STATUS_PROCERR;
3303 else
3304 status = HCHK_STATUS_PROCOK;
3305 } else if (expired) {
3306 status = HCHK_STATUS_PROCTOUT;
3307 ha_warning("kill %d\n", (int)elem->pid);
3308 kill(elem->pid, SIGTERM);
3309 }
3310 set_server_check_status(check, status, NULL);
3311 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003312
Christopher Faulet61cc8522020-04-20 14:54:42 +02003313 if (check->result == CHK_RES_FAILED) {
3314 /* a failure or timeout detected */
3315 check_notify_failure(check);
3316 }
3317 else if (check->result == CHK_RES_CONDPASS) {
3318 /* check is OK but asks for stopping mode */
3319 check_notify_stopping(check);
3320 }
3321 else if (check->result == CHK_RES_PASSED) {
3322 /* a success was detected */
3323 check_notify_success(check);
3324 }
3325 task_set_affinity(t, 1);
3326 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003327
Christopher Faulet61cc8522020-04-20 14:54:42 +02003328 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003329
Christopher Faulet61cc8522020-04-20 14:54:42 +02003330 rv = 0;
3331 if (global.spread_checks > 0) {
3332 rv = srv_getinter(check) * global.spread_checks / 100;
3333 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3334 }
3335 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3336 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003337
Christopher Faulet61cc8522020-04-20 14:54:42 +02003338 reschedule:
3339 while (tick_is_expired(t->expire, now_ms))
3340 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003341
Christopher Faulet61cc8522020-04-20 14:54:42 +02003342 out_unlock:
3343 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3344 return t;
3345}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003346
Baptiste Assmann248f1172018-03-01 21:49:01 +01003347
Christopher Faulet61cc8522020-04-20 14:54:42 +02003348/**************************************************************************/
3349/***************** Health-checks based on connections *********************/
3350/**************************************************************************/
3351/* This function is used only for server health-checks. It handles connection
3352 * status updates including errors. If necessary, it wakes the check task up.
3353 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3354 * connection (eg: reconnect). It relies on tcpcheck_main().
3355 */
3356static int wake_srv_chk(struct conn_stream *cs)
3357{
3358 struct connection *conn = cs->conn;
3359 struct check *check = cs->data;
3360 struct email_alertq *q = container_of(check, typeof(*q), check);
3361 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003362
Christopher Faulet61cc8522020-04-20 14:54:42 +02003363 if (check->server)
3364 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3365 else
3366 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003367
Christopher Faulet61cc8522020-04-20 14:54:42 +02003368 /* we may have to make progress on the TCP checks */
3369 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003370
Christopher Faulet61cc8522020-04-20 14:54:42 +02003371 cs = check->cs;
3372 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003373
Christopher Faulet61cc8522020-04-20 14:54:42 +02003374 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3375 /* We may get error reports bypassing the I/O handlers, typically
3376 * the case when sending a pure TCP check which fails, then the I/O
3377 * handlers above are not called. This is completely handled by the
3378 * main processing task so let's simply wake it up. If we get here,
3379 * we expect errno to still be valid.
3380 */
3381 chk_report_conn_err(check, errno, 0);
3382 task_wakeup(check->task, TASK_WOKEN_IO);
3383 }
3384
3385 if (check->result != CHK_RES_UNKNOWN) {
3386 /* Check complete or aborted. If connection not yet closed do it
3387 * now and wake the check task up to be sure the result is
3388 * handled ASAP. */
3389 conn_sock_drain(conn);
3390 cs_close(cs);
3391 ret = -1;
3392 /* We may have been scheduled to run, and the
3393 * I/O handler expects to have a cs, so remove
3394 * the tasklet
3395 */
3396 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3397 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003398 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003399
3400 if (check->server)
3401 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003402 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003403 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003404
Christopher Faulet61cc8522020-04-20 14:54:42 +02003405 /* if a connection got replaced, we must absolutely prevent the connection
3406 * handler from touching its fd, and perform the FD polling updates ourselves
3407 */
3408 if (ret < 0)
3409 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003410
Christopher Faulet61cc8522020-04-20 14:54:42 +02003411 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003412}
3413
Christopher Faulet61cc8522020-04-20 14:54:42 +02003414/* This function checks if any I/O is wanted, and if so, attempts to do so */
3415static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003416{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003417 struct check *check = ctx;
3418 struct conn_stream *cs = check->cs;
3419 struct email_alertq *q = container_of(check, typeof(*q), check);
3420 int ret = 0;
Simon Hormanb1900d52015-01-30 11:22:54 +09003421
Christopher Faulet61cc8522020-04-20 14:54:42 +02003422 if (!(check->wait_list.events & SUB_RETRY_SEND))
3423 ret = wake_srv_chk(cs);
3424 if (ret == 0 && !(check->wait_list.events & SUB_RETRY_RECV)) {
3425 if (check->server)
3426 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3427 else
3428 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Hormanb1900d52015-01-30 11:22:54 +09003429
Christopher Faulet61cc8522020-04-20 14:54:42 +02003430 if (unlikely(check->result == CHK_RES_FAILED)) {
3431 /* collect possible new errors */
3432 if (cs->conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
3433 chk_report_conn_err(check, 0, 0);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003434
Christopher Faulet61cc8522020-04-20 14:54:42 +02003435 /* Reset the check buffer... */
3436 b_reset(&check->bi);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003437
Christopher Faulet61cc8522020-04-20 14:54:42 +02003438 /* Close the connection... We still attempt to nicely close if,
3439 * for instance, SSL needs to send a "close notify." Later, we perform
3440 * a hard close and reset the connection if some data are pending,
3441 * otherwise we end up with many TIME_WAITs and eat all the source port
3442 * range quickly. To avoid sending RSTs all the time, we first try to
3443 * drain pending data.
3444 */
3445 /* Call cs_shutr() first, to add the CO_FL_SOCK_RD_SH flag on the
3446 * connection, to make sure cs_shutw() will not lead to a shutdown()
3447 * that would provoke TIME_WAITs.
3448 */
3449 cs_shutr(cs, CS_SHR_DRAIN);
3450 cs_shutw(cs, CS_SHW_NORMAL);
Simon Hormanb1900d52015-01-30 11:22:54 +09003451
Christopher Faulet61cc8522020-04-20 14:54:42 +02003452 /* OK, let's not stay here forever */
3453 if (check->result == CHK_RES_FAILED)
3454 cs->conn->flags |= CO_FL_ERROR;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01003455
Christopher Faulet61cc8522020-04-20 14:54:42 +02003456 task_wakeup(t, TASK_WOKEN_IO);
3457 }
3458
3459 if (check->server)
3460 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3461 else
3462 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet23d86d12018-01-25 11:36:35 +01003463 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003464 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003465}
3466
Christopher Faulet61cc8522020-04-20 14:54:42 +02003467/* manages a server health-check that uses a connection. Returns
3468 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3469 *
3470 * Please do NOT place any return statement in this function and only leave
3471 * via the out_unlock label.
3472 */
3473static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003474{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003475 struct check *check = context;
3476 struct proxy *proxy = check->proxy;
3477 struct conn_stream *cs = check->cs;
3478 struct connection *conn = cs_conn(cs);
3479 int rv;
3480 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003481
Christopher Faulet61cc8522020-04-20 14:54:42 +02003482 if (check->server)
3483 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3484 if (!(check->state & CHK_ST_INPROGRESS)) {
3485 /* no check currently running */
3486 if (!expired) /* woke up too early */
3487 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003488
Christopher Faulet61cc8522020-04-20 14:54:42 +02003489 /* we don't send any health-checks when the proxy is
3490 * stopped, the server should not be checked or the check
3491 * is disabled.
3492 */
3493 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3494 proxy->state == PR_STSTOPPED)
3495 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003496
Christopher Faulet61cc8522020-04-20 14:54:42 +02003497 /* we'll initiate a new check */
3498 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003499
Christopher Faulet61cc8522020-04-20 14:54:42 +02003500 check->state |= CHK_ST_INPROGRESS;
3501 b_reset(&check->bi);
3502 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003503
Christopher Faulet61cc8522020-04-20 14:54:42 +02003504 task_set_affinity(t, tid_bit);
3505 cs = check->cs;
3506 conn = cs_conn(cs);
3507 if (!conn) {
3508 check->current_step = NULL;
3509 tcpcheck_main(check);
3510 goto out_unlock;
3511 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003512
Christopher Faulet61cc8522020-04-20 14:54:42 +02003513 conn->flags |= CO_FL_ERROR;
3514 chk_report_conn_err(check, 0, 0);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003515
Christopher Faulet61cc8522020-04-20 14:54:42 +02003516 /* here, we have seen a synchronous error, no fd was allocated */
3517 task_set_affinity(t, MAX_THREADS_MASK);
3518 if (cs) {
3519 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003520 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003521 /* We may have been scheduled to run, and the
3522 * I/O handler expects to have a cs, so remove
3523 * the tasklet
3524 */
3525 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3526 cs_destroy(cs);
3527 cs = check->cs = NULL;
3528 conn = NULL;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003529 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003530
Christopher Faulet61cc8522020-04-20 14:54:42 +02003531 check->state &= ~CHK_ST_INPROGRESS;
3532 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003533
Christopher Faulet61cc8522020-04-20 14:54:42 +02003534 /* we allow up to min(inter, timeout.connect) for a connection
3535 * to establish but only when timeout.check is set
3536 * as it may be to short for a full check otherwise
3537 */
3538 while (tick_is_expired(t->expire, now_ms)) {
3539 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003540
Christopher Faulet61cc8522020-04-20 14:54:42 +02003541 t_con = tick_add(t->expire, proxy->timeout.connect);
3542 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3543 if (proxy->timeout.check)
3544 t->expire = tick_first(t->expire, t_con);
3545 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003546 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003547 else {
3548 /* there was a test running.
3549 * First, let's check whether there was an uncaught error,
3550 * which can happen on connect timeout or error.
3551 */
3552 if (check->result == CHK_RES_UNKNOWN) {
3553 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3554 chk_report_conn_err(check, 0, expired);
3555 }
3556 else
3557 goto out_unlock; /* timeout not reached, wait again */
3558 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003559
Christopher Faulet61cc8522020-04-20 14:54:42 +02003560 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003561
Christopher Faulet61cc8522020-04-20 14:54:42 +02003562 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003563
Christopher Faulet61cc8522020-04-20 14:54:42 +02003564 if (conn && conn->xprt) {
3565 /* The check was aborted and the connection was not yet closed.
3566 * This can happen upon timeout, or when an external event such
3567 * as a failed response coupled with "observe layer7" caused the
3568 * server state to be suddenly changed.
3569 */
3570 conn_sock_drain(conn);
3571 cs_close(cs);
3572 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003573
Christopher Faulet61cc8522020-04-20 14:54:42 +02003574 if (cs) {
3575 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003576 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003577 /* We may have been scheduled to run, and the
3578 * I/O handler expects to have a cs, so remove
3579 * the tasklet
3580 */
3581 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3582 cs_destroy(cs);
3583 cs = check->cs = NULL;
3584 conn = NULL;
3585 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003586
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003587 if (check->sess != NULL) {
3588 vars_prune(&check->vars, check->sess, NULL);
3589 session_free(check->sess);
3590 check->sess = NULL;
3591 }
3592
Christopher Faulet61cc8522020-04-20 14:54:42 +02003593 if (check->server) {
3594 if (check->result == CHK_RES_FAILED) {
3595 /* a failure or timeout detected */
3596 check_notify_failure(check);
3597 }
3598 else if (check->result == CHK_RES_CONDPASS) {
3599 /* check is OK but asks for stopping mode */
3600 check_notify_stopping(check);
3601 }
3602 else if (check->result == CHK_RES_PASSED) {
3603 /* a success was detected */
3604 check_notify_success(check);
3605 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003606 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003607 task_set_affinity(t, MAX_THREADS_MASK);
3608 check->state &= ~CHK_ST_INPROGRESS;
3609
3610 if (check->server) {
3611 rv = 0;
3612 if (global.spread_checks > 0) {
3613 rv = srv_getinter(check) * global.spread_checks / 100;
3614 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3615 }
3616 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003617 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003618 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003619
Christopher Faulet61cc8522020-04-20 14:54:42 +02003620 reschedule:
3621 while (tick_is_expired(t->expire, now_ms))
3622 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3623 out_unlock:
3624 if (check->server)
3625 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3626 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003627}
3628
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003629
Christopher Faulet61cc8522020-04-20 14:54:42 +02003630/**************************************************************************/
3631/******************* Internals to parse tcp-check rules *******************/
3632/**************************************************************************/
3633struct action_kw_list tcp_check_keywords = {
3634 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3635};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003636
Christopher Faulet61cc8522020-04-20 14:54:42 +02003637/* Return the struct action_kw associated to a keyword */
3638static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003639{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003640 return action_lookup(&tcp_check_keywords.list, kw);
3641}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003642
Christopher Faulet61cc8522020-04-20 14:54:42 +02003643static void action_kw_tcp_check_build_list(struct buffer *chk)
3644{
3645 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003646}
3647
Christopher Faulet61cc8522020-04-20 14:54:42 +02003648/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3649 * returned on error.
3650 */
3651static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3652 struct list *rules, struct action_kw *kw,
3653 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003654{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003655 struct tcpcheck_rule *chk = NULL;
3656 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003657
Christopher Faulet61cc8522020-04-20 14:54:42 +02003658 actrule = calloc(1, sizeof(*actrule));
3659 if (!actrule) {
3660 memprintf(errmsg, "out of memory");
3661 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003662 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003663 actrule->kw = kw;
3664 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003665
Christopher Faulet61cc8522020-04-20 14:54:42 +02003666 cur_arg++;
3667 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3668 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3669 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003670 }
3671
Christopher Faulet61cc8522020-04-20 14:54:42 +02003672 chk = calloc(1, sizeof(*chk));
3673 if (!chk) {
3674 memprintf(errmsg, "out of memory");
3675 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003676 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003677 chk->action = TCPCHK_ACT_ACTION_KW;
3678 chk->action_kw.rule = actrule;
3679 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003680
3681 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003682 free(actrule);
3683 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003684}
3685
Christopher Faulet61cc8522020-04-20 14:54:42 +02003686/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3687 * returned on error.
3688 */
3689static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3690 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003691{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003692 struct tcpcheck_rule *chk = NULL;
3693 struct sockaddr_storage *sk = NULL;
3694 char *comment = NULL, *sni = NULL, *alpn = NULL;
3695 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003696 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003697 unsigned short conn_opts = 0;
3698 long port = 0;
3699 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003700
Christopher Faulet61cc8522020-04-20 14:54:42 +02003701 list_for_each_entry(chk, rules, list) {
3702 if (chk->action == TCPCHK_ACT_CONNECT)
3703 break;
3704 if (chk->action == TCPCHK_ACT_COMMENT ||
3705 chk->action == TCPCHK_ACT_ACTION_KW ||
3706 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3707 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003708
Christopher Faulet61cc8522020-04-20 14:54:42 +02003709 memprintf(errmsg, "first step MUST also be a 'connect', "
3710 "optionnaly preceded by a 'set-var', an 'unset-var' or a 'comment', "
3711 "when there is a 'connect' step in the tcp-check ruleset");
3712 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003713 }
3714
Christopher Faulet61cc8522020-04-20 14:54:42 +02003715 cur_arg++;
3716 while (*(args[cur_arg])) {
3717 if (strcmp(args[cur_arg], "default") == 0)
3718 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3719 else if (strcmp(args[cur_arg], "addr") == 0) {
3720 int port1, port2;
3721 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003722
Christopher Faulet61cc8522020-04-20 14:54:42 +02003723 if (!*(args[cur_arg+1])) {
3724 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3725 goto error;
3726 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003727
Christopher Faulet61cc8522020-04-20 14:54:42 +02003728 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3729 if (!sk) {
3730 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3731 goto error;
3732 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003733
Christopher Faulet61cc8522020-04-20 14:54:42 +02003734 proto = protocol_by_family(sk->ss_family);
3735 if (!proto || !proto->connect) {
3736 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3737 args[cur_arg]);
3738 goto error;
3739 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003740
Christopher Faulet61cc8522020-04-20 14:54:42 +02003741 if (port1 != port2) {
3742 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3743 args[cur_arg], args[cur_arg+1]);
3744 goto error;
3745 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003746
Christopher Faulet61cc8522020-04-20 14:54:42 +02003747 cur_arg++;
3748 }
3749 else if (strcmp(args[cur_arg], "port") == 0) {
3750 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003751
Christopher Faulet61cc8522020-04-20 14:54:42 +02003752 if (!*(args[cur_arg+1])) {
3753 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3754 goto error;
3755 }
3756 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003757
Christopher Faulet61cc8522020-04-20 14:54:42 +02003758 port = 0;
3759 release_sample_expr(port_expr);
3760 p = args[cur_arg]; end = p + strlen(p);
3761 port = read_uint(&p, end);
3762 if (p != end) {
3763 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003764
Christopher Faulet61cc8522020-04-20 14:54:42 +02003765 px->conf.args.ctx = ARGC_SRV;
3766 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3767 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003768
Christopher Faulet61cc8522020-04-20 14:54:42 +02003769 if (!port_expr) {
3770 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3771 goto error;
3772 }
3773 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3774 memprintf(errmsg, "error detected while parsing port expression : "
3775 " fetch method '%s' extracts information from '%s', "
3776 "none of which is available here.\n",
3777 args[cur_arg], sample_src_names(port_expr->fetch->use));
3778 goto error;
3779 }
3780 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3781 }
3782 else if (port > 65535 || port < 1) {
3783 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3784 args[cur_arg]);
3785 goto error;
3786 }
3787 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003788 else if (strcmp(args[cur_arg], "proto") == 0) {
3789 if (!*(args[cur_arg+1])) {
3790 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3791 goto error;
3792 }
3793 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3794 if (!mux_proto) {
3795 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3796 goto error;
3797 }
3798 cur_arg++;
3799 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003800 else if (strcmp(args[cur_arg], "comment") == 0) {
3801 if (!*(args[cur_arg+1])) {
3802 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3803 goto error;
3804 }
3805 cur_arg++;
3806 free(comment);
3807 comment = strdup(args[cur_arg]);
3808 if (!comment) {
3809 memprintf(errmsg, "out of memory");
3810 goto error;
3811 }
3812 }
3813 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3814 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3815 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3816 conn_opts |= TCPCHK_OPT_SOCKS4;
3817 else if (strcmp(args[cur_arg], "linger") == 0)
3818 conn_opts |= TCPCHK_OPT_LINGER;
3819#ifdef USE_OPENSSL
3820 else if (strcmp(args[cur_arg], "ssl") == 0) {
3821 px->options |= PR_O_TCPCHK_SSL;
3822 conn_opts |= TCPCHK_OPT_SSL;
3823 }
3824 else if (strcmp(args[cur_arg], "sni") == 0) {
3825 if (!*(args[cur_arg+1])) {
3826 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3827 goto error;
3828 }
3829 cur_arg++;
3830 free(sni);
3831 sni = strdup(args[cur_arg]);
3832 if (!sni) {
3833 memprintf(errmsg, "out of memory");
3834 goto error;
3835 }
3836 }
3837 else if (strcmp(args[cur_arg], "alpn") == 0) {
3838#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3839 free(alpn);
3840 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3841 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3842 goto error;
3843 }
3844 cur_arg++;
3845#else
3846 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003847 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003848#endif
3849 }
3850#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003851
Christopher Faulet61cc8522020-04-20 14:54:42 +02003852 else {
3853 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3854#ifdef USE_OPENSSL
3855 ", 'ssl', 'sni', 'alpn'"
3856#endif /* USE_OPENSSL */
3857 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3858 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003859 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003860 }
3861 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003862 }
3863
Christopher Faulet61cc8522020-04-20 14:54:42 +02003864 chk = calloc(1, sizeof(*chk));
3865 if (!chk) {
3866 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003867 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003868 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003869 chk->action = TCPCHK_ACT_CONNECT;
3870 chk->comment = comment;
3871 chk->connect.port = port;
3872 chk->connect.options = conn_opts;
3873 chk->connect.sni = sni;
3874 chk->connect.alpn = alpn;
3875 chk->connect.alpn_len= alpn_len;
3876 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003877 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003878 if (sk)
3879 chk->connect.addr = *sk;
3880 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003881
Christopher Faulet61cc8522020-04-20 14:54:42 +02003882 error:
3883 free(alpn);
3884 free(sni);
3885 free(comment);
3886 release_sample_expr(port_expr);
3887 return NULL;
3888}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003889
Christopher Faulet61cc8522020-04-20 14:54:42 +02003890/* Parses and creates a tcp-check send rule. NULL is returned on error */
3891static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3892 const char *file, int line, char **errmsg)
3893{
3894 struct tcpcheck_rule *chk = NULL;
3895 char *comment = NULL, *data = NULL;
3896 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003897
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003898 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3899 type = TCPCHK_SEND_BINARY_LF;
3900 else if (strcmp(args[cur_arg], "send-binary") == 0)
3901 type = TCPCHK_SEND_BINARY;
3902 else if (strcmp(args[cur_arg], "send-lf") == 0)
3903 type = TCPCHK_SEND_STRING_LF;
3904 else if (strcmp(args[cur_arg], "send") == 0)
3905 type = TCPCHK_SEND_STRING;
3906
Christopher Faulet61cc8522020-04-20 14:54:42 +02003907 if (!*(args[cur_arg+1])) {
3908 memprintf(errmsg, "'%s' expects a %s as argument",
3909 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003910 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003911 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003912
Christopher Faulet61cc8522020-04-20 14:54:42 +02003913 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003914
Christopher Faulet61cc8522020-04-20 14:54:42 +02003915 cur_arg += 2;
3916 while (*(args[cur_arg])) {
3917 if (strcmp(args[cur_arg], "comment") == 0) {
3918 if (!*(args[cur_arg+1])) {
3919 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3920 goto error;
3921 }
3922 cur_arg++;
3923 free(comment);
3924 comment = strdup(args[cur_arg]);
3925 if (!comment) {
3926 memprintf(errmsg, "out of memory");
3927 goto error;
3928 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003929 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003930 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003931 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003932 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003933 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003934 }
3935 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003936 }
3937
Christopher Faulet61cc8522020-04-20 14:54:42 +02003938 chk = calloc(1, sizeof(*chk));
3939 if (!chk) {
3940 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003941 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003942 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003943 chk->action = TCPCHK_ACT_SEND;
3944 chk->comment = comment;
3945 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003946
Christopher Faulet61cc8522020-04-20 14:54:42 +02003947 switch (chk->send.type) {
3948 case TCPCHK_SEND_STRING:
3949 chk->send.data = ist2(strdup(data), strlen(data));
3950 if (!isttest(chk->send.data)) {
3951 memprintf(errmsg, "out of memory");
3952 goto error;
3953 }
3954 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003955 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003956 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003957 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003958 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3959 goto error;
3960 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003961 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003962 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003963 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003964 case TCPCHK_SEND_STRING_LF:
3965 case TCPCHK_SEND_BINARY_LF:
3966 LIST_INIT(&chk->send.fmt);
3967 px->conf.args.ctx = ARGC_SRV;
3968 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3969 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3970 goto error;
3971 }
3972 break;
3973 case TCPCHK_SEND_HTTP:
3974 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003975 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003976 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003977
Christopher Faulet61cc8522020-04-20 14:54:42 +02003978 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003979
Christopher Faulet61cc8522020-04-20 14:54:42 +02003980 error:
3981 free(chk);
3982 free(comment);
3983 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003984}
3985
Christopher Faulet61cc8522020-04-20 14:54:42 +02003986/* Parses and creates a http-check send rule. NULL is returned on error */
3987static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3988 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003989{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003990 struct tcpcheck_rule *chk = NULL;
3991 struct tcpcheck_http_hdr *hdr = NULL;
3992 struct http_hdr hdrs[global.tune.max_http_hdr];
3993 char *meth = NULL, *uri = NULL, *vsn = NULL;
3994 char *body = NULL, *comment = NULL;
3995 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003996 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003997
3998 cur_arg++;
3999 while (*(args[cur_arg])) {
4000 if (strcmp(args[cur_arg], "meth") == 0) {
4001 if (!*(args[cur_arg+1])) {
4002 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4003 goto error;
4004 }
4005 cur_arg++;
4006 meth = args[cur_arg];
4007 }
4008 else if (strcmp(args[cur_arg], "uri") == 0) {
4009 if (!*(args[cur_arg+1])) {
4010 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4011 goto error;
4012 }
4013 cur_arg++;
4014 uri = args[cur_arg];
4015 // TODO: log-format uri
4016 }
Christopher Faulet907701b2020-04-28 09:37:00 +02004017 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004018 if (!*(args[cur_arg+1])) {
4019 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4020 goto error;
4021 }
4022 cur_arg++;
4023 vsn = args[cur_arg];
4024 }
4025 else if (strcmp(args[cur_arg], "hdr") == 0) {
4026 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4027 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4028 goto error;
4029 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004030
4031 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4032 if (host_hdr >= 0) {
4033 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4034 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4035 goto error;
4036 }
4037 host_hdr = i;
4038 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004039 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4040 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4041 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4042 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004043
Christopher Faulet61cc8522020-04-20 14:54:42 +02004044 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4045 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4046 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004047 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004048 cur_arg += 2;
4049 }
4050 else if (strcmp(args[cur_arg], "body") == 0) {
4051 if (!*(args[cur_arg+1])) {
4052 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4053 goto error;
4054 }
4055 cur_arg++;
4056 body = args[cur_arg];
4057 // TODO: log-format body
4058 }
4059 else if (strcmp(args[cur_arg], "comment") == 0) {
4060 if (!*(args[cur_arg+1])) {
4061 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4062 goto error;
4063 }
4064 cur_arg++;
4065 free(comment);
4066 comment = strdup(args[cur_arg]);
4067 if (!comment) {
4068 memprintf(errmsg, "out of memory");
4069 goto error;
4070 }
4071 }
4072 else {
Christopher Faulet907701b2020-04-28 09:37:00 +02004073 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'ver', 'hdr' and 'body' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02004074 args[cur_arg]);
4075 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004076 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004077 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004078 }
4079
Christopher Faulet61cc8522020-04-20 14:54:42 +02004080 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004081
Christopher Faulet61cc8522020-04-20 14:54:42 +02004082 chk = calloc(1, sizeof(*chk));
4083 if (!chk) {
4084 memprintf(errmsg, "out of memory");
4085 goto error;
4086 }
4087 chk->action = TCPCHK_ACT_SEND;
4088 chk->comment = comment; comment = NULL;
4089 chk->send.type = TCPCHK_SEND_HTTP;
4090 chk->send.http.flags = flags;
4091 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004092
Christopher Faulet61cc8522020-04-20 14:54:42 +02004093 if (meth) {
4094 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4095 chk->send.http.meth.str.area = strdup(meth);
4096 chk->send.http.meth.str.data = strlen(meth);
4097 if (!chk->send.http.meth.str.area) {
4098 memprintf(errmsg, "out of memory");
4099 goto error;
4100 }
4101 }
4102 if (uri) {
4103 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4104 if (!isttest(chk->send.http.uri)) {
4105 memprintf(errmsg, "out of memory");
4106 goto error;
4107 }
4108 }
4109 if (vsn) {
4110 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4111 if (!isttest(chk->send.http.vsn)) {
4112 memprintf(errmsg, "out of memory");
4113 goto error;
4114 }
4115 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004116 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004117 hdr = calloc(1, sizeof(*hdr));
4118 if (!hdr) {
4119 memprintf(errmsg, "out of memory");
4120 goto error;
4121 }
4122 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004123 hdr->name = istdup(hdrs[i].n);
4124 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004125 memprintf(errmsg, "out of memory");
4126 goto error;
4127 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004128
Christopher Fauletb61caf42020-04-21 10:57:42 +02004129 ist0(hdrs[i].v);
4130 if (!parse_logformat_string(istptr(hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
Christopher Faulet61cc8522020-04-20 14:54:42 +02004131 goto error;
4132 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4133 hdr = NULL;
4134 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004135
Christopher Faulet61cc8522020-04-20 14:54:42 +02004136 if (body) {
4137 chk->send.http.body = ist2(strdup(body), strlen(body));
4138 if (!isttest(chk->send.http.body)) {
4139 memprintf(errmsg, "out of memory");
4140 goto error;
4141 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004142 }
4143
Christopher Faulet61cc8522020-04-20 14:54:42 +02004144 return chk;
4145
4146 error:
4147 free_tcpcheck_http_hdr(hdr);
4148 free_tcpcheck(chk, 0);
4149 free(comment);
4150 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004151}
4152
Christopher Faulet61cc8522020-04-20 14:54:42 +02004153/* Parses and creates a http-check comment rule. NULL is returned on error */
4154static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4155 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004156{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004157 struct tcpcheck_rule *chk = NULL;
4158 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004159
Christopher Faulet61cc8522020-04-20 14:54:42 +02004160 if (!*(args[cur_arg+1])) {
4161 memprintf(errmsg, "expects a string as argument");
4162 goto error;
4163 }
4164 cur_arg++;
4165 comment = strdup(args[cur_arg]);
4166 if (!comment) {
4167 memprintf(errmsg, "out of memory");
4168 goto error;
4169 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004170
Christopher Faulet61cc8522020-04-20 14:54:42 +02004171 chk = calloc(1, sizeof(*chk));
4172 if (!chk) {
4173 memprintf(errmsg, "out of memory");
4174 goto error;
4175 }
4176 chk->action = TCPCHK_ACT_COMMENT;
4177 chk->comment = comment;
4178 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004179
Christopher Faulet61cc8522020-04-20 14:54:42 +02004180 error:
4181 free(comment);
4182 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004183}
4184
Christopher Faulet61cc8522020-04-20 14:54:42 +02004185/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4186 * on error. <proto> is set to the right protocol flags (covered by the
4187 * TCPCHK_RULES_PROTO_CHK mask).
4188 */
4189static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4190 struct list *rules, unsigned int proto,
4191 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004192{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004193 struct tcpcheck_rule *prev_check, *chk = NULL;
4194 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004195 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004196 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004197 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4198 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4199 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004200 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004201 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004202 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004203
Christopher Faulet39708192020-05-05 10:47:36 +02004204 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004205 if (!*(args[cur_arg+1])) {
4206 memprintf(errmsg, "expects at least a matching pattern as arguments");
4207 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004208 }
4209
Christopher Faulet61cc8522020-04-20 14:54:42 +02004210 cur_arg++;
4211 while (*(args[cur_arg])) {
4212 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004213
Christopher Faulet61cc8522020-04-20 14:54:42 +02004214 rescan:
4215 if (strcmp(args[cur_arg], "min-recv") == 0) {
4216 if (in_pattern) {
4217 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4218 goto error;
4219 }
4220 if (!*(args[cur_arg+1])) {
4221 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4222 goto error;
4223 }
4224 /* Use an signed integer here because of chksize */
4225 cur_arg++;
4226 min_recv = atol(args[cur_arg]);
4227 if (min_recv < -1 || min_recv > INT_MAX) {
4228 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4229 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004230 }
4231 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004232 else if (*(args[cur_arg]) == '!') {
4233 in_pattern = 1;
4234 while (*(args[cur_arg]) == '!') {
4235 inverse = !inverse;
4236 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004237 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004238 if (!*(args[cur_arg]))
4239 cur_arg++;
4240 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004241 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004242 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4243 if (type != TCPCHK_EXPECT_UNDEF) {
4244 memprintf(errmsg, "only on pattern expected");
4245 goto error;
4246 }
4247 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004248 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004249 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004250 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004251
Christopher Faulet61cc8522020-04-20 14:54:42 +02004252 if (!*(args[cur_arg+1])) {
4253 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4254 goto error;
4255 }
4256 cur_arg++;
4257 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004258 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004259 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4260 if (proto == TCPCHK_RULES_HTTP_CHK)
4261 goto bad_http_kw;
4262 if (type != TCPCHK_EXPECT_UNDEF) {
4263 memprintf(errmsg, "only on pattern expected");
4264 goto error;
4265 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004266 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004267
Christopher Faulet61cc8522020-04-20 14:54:42 +02004268 if (!*(args[cur_arg+1])) {
4269 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4270 goto error;
4271 }
4272 cur_arg++;
4273 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004274 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004275 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4276 if (type != TCPCHK_EXPECT_UNDEF) {
4277 memprintf(errmsg, "only on pattern expected");
4278 goto error;
4279 }
4280 if (proto != TCPCHK_RULES_HTTP_CHK)
4281 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4282 else {
4283 if (*(args[cur_arg]) != 's')
4284 goto bad_http_kw;
4285 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4286 }
4287
4288 if (!*(args[cur_arg+1])) {
4289 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4290 goto error;
4291 }
4292 cur_arg++;
4293 pattern = args[cur_arg];
4294 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004295 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4296 if (proto != TCPCHK_RULES_HTTP_CHK)
4297 goto bad_tcp_kw;
4298 if (type != TCPCHK_EXPECT_UNDEF) {
4299 memprintf(errmsg, "only on pattern expected");
4300 goto error;
4301 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004302 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004303
Christopher Faulet61cc8522020-04-20 14:54:42 +02004304 if (!*(args[cur_arg+1])) {
4305 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4306 goto error;
4307 }
4308 cur_arg++;
4309 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004310 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004311 else if (strcmp(args[cur_arg], "custom") == 0) {
4312 if (in_pattern) {
4313 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4314 goto error;
4315 }
4316 if (type != TCPCHK_EXPECT_UNDEF) {
4317 memprintf(errmsg, "only on pattern expected");
4318 goto error;
4319 }
4320 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004321 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004322 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004323 int orig_arg = cur_arg;
4324
4325 if (proto != TCPCHK_RULES_HTTP_CHK)
4326 goto bad_tcp_kw;
4327 if (type != TCPCHK_EXPECT_UNDEF) {
4328 memprintf(errmsg, "only on pattern expected");
4329 goto error;
4330 }
4331 type = TCPCHK_EXPECT_HTTP_HEADER;
4332
Christopher Fauletb5594262020-05-05 20:23:13 +02004333 if (strcmp(args[cur_arg], "fhdr") == 0)
4334 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4335
Christopher Faulet39708192020-05-05 10:47:36 +02004336 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004337 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4338 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4339 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004340 args[orig_arg]);
4341 goto error;
4342 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004343
4344 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4345 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4346
Christopher Faulet39708192020-05-05 10:47:36 +02004347 cur_arg += 2;
4348 if (strcmp(args[cur_arg], "-m") == 0) {
4349 if (!*(args[cur_arg+1])) {
4350 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4351 args[orig_arg], args[cur_arg]);
4352 goto error;
4353 }
4354 if (strcmp(args[cur_arg+1], "str") == 0)
4355 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4356 else if (strcmp(args[cur_arg+1], "beg") == 0)
4357 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4358 else if (strcmp(args[cur_arg+1], "end") == 0)
4359 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4360 else if (strcmp(args[cur_arg+1], "sub") == 0)
4361 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004362 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4363 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4364 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4365 args[orig_arg]);
4366 goto error;
4367 }
Christopher Faulet39708192020-05-05 10:47:36 +02004368 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004369 }
Christopher Faulet39708192020-05-05 10:47:36 +02004370 else {
4371 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4372 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4373 goto error;
4374 }
4375 cur_arg += 2;
4376 }
4377 else
4378 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4379 npat = args[cur_arg];
4380
Christopher Fauletb5594262020-05-05 20:23:13 +02004381 if (!*(args[cur_arg+1]) ||
4382 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004383 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4384 goto next;
4385 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004386 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4387 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004388
4389 /* Parse the value pattern, optionnal */
Christopher Fauletb5594262020-05-05 20:23:13 +02004390 if (strcmp(args[cur_arg+2], "-m") == 0) {
4391 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004392 if (!*(args[cur_arg+1])) {
4393 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4394 args[orig_arg], args[cur_arg]);
4395 goto error;
4396 }
4397 if (strcmp(args[cur_arg+1], "str") == 0)
4398 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4399 else if (strcmp(args[cur_arg+1], "beg") == 0)
4400 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4401 else if (strcmp(args[cur_arg+1], "end") == 0)
4402 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4403 else if (strcmp(args[cur_arg+1], "sub") == 0)
4404 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004405 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4406 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4407 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4408 args[orig_arg]);
4409 goto error;
4410 }
Christopher Faulet39708192020-05-05 10:47:36 +02004411 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004412 }
Christopher Faulet39708192020-05-05 10:47:36 +02004413 else {
4414 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4415 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4416 goto error;
4417 }
Christopher Faulet39708192020-05-05 10:47:36 +02004418 }
4419 else
4420 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004421
Christopher Fauletb5594262020-05-05 20:23:13 +02004422 if (!*(args[cur_arg+2])) {
4423 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4424 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004425 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004426 vpat = args[cur_arg+2];
4427 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004428 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004429 else if (strcmp(args[cur_arg], "comment") == 0) {
4430 if (in_pattern) {
4431 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4432 goto error;
4433 }
4434 if (!*(args[cur_arg+1])) {
4435 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4436 goto error;
4437 }
4438 cur_arg++;
4439 free(comment);
4440 comment = strdup(args[cur_arg]);
4441 if (!comment) {
4442 memprintf(errmsg, "out of memory");
4443 goto error;
4444 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004445 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004446 else if (strcmp(args[cur_arg], "on-success") == 0) {
4447 if (in_pattern) {
4448 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4449 goto error;
4450 }
4451 if (!*(args[cur_arg+1])) {
4452 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4453 goto error;
4454 }
4455 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004456 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004457 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004458 else if (strcmp(args[cur_arg], "on-error") == 0) {
4459 if (in_pattern) {
4460 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4461 goto error;
4462 }
4463 if (!*(args[cur_arg+1])) {
4464 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4465 goto error;
4466 }
4467 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004468 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004469 }
4470 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4471 if (in_pattern) {
4472 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4473 goto error;
4474 }
4475 if (!*(args[cur_arg+1])) {
4476 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4477 goto error;
4478 }
4479 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4480 ok_st = HCHK_STATUS_L7OKD;
4481 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4482 ok_st = HCHK_STATUS_L7OKCD;
4483 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4484 ok_st = HCHK_STATUS_L6OK;
4485 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4486 ok_st = HCHK_STATUS_L4OK;
4487 else {
4488 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4489 args[cur_arg], args[cur_arg+1]);
4490 goto error;
4491 }
4492 cur_arg++;
4493 }
4494 else if (strcmp(args[cur_arg], "error-status") == 0) {
4495 if (in_pattern) {
4496 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4497 goto error;
4498 }
4499 if (!*(args[cur_arg+1])) {
4500 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4501 goto error;
4502 }
4503 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4504 err_st = HCHK_STATUS_L7RSP;
4505 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4506 err_st = HCHK_STATUS_L7STS;
4507 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4508 err_st = HCHK_STATUS_L6RSP;
4509 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4510 err_st = HCHK_STATUS_L4CON;
4511 else {
4512 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4513 args[cur_arg], args[cur_arg+1]);
4514 goto error;
4515 }
4516 cur_arg++;
4517 }
4518 else if (strcmp(args[cur_arg], "status-code") == 0) {
4519 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004520
Christopher Faulet61cc8522020-04-20 14:54:42 +02004521 if (in_pattern) {
4522 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4523 goto error;
4524 }
4525 if (!*(args[cur_arg+1])) {
4526 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4527 goto error;
4528 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004529
Christopher Faulet61cc8522020-04-20 14:54:42 +02004530 cur_arg++;
4531 release_sample_expr(status_expr);
4532 px->conf.args.ctx = ARGC_SRV;
4533 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4534 file, line, errmsg, &px->conf.args, NULL);
4535 if (!status_expr) {
4536 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4537 goto error;
4538 }
4539 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4540 memprintf(errmsg, "error detected while parsing status-code expression : "
4541 " fetch method '%s' extracts information from '%s', "
4542 "none of which is available here.\n",
4543 args[cur_arg], sample_src_names(status_expr->fetch->use));
4544 goto error;
4545 }
4546 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4547 }
4548 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4549 if (in_pattern) {
4550 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4551 goto error;
4552 }
4553 if (!*(args[cur_arg+1])) {
4554 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4555 goto error;
4556 }
4557 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4558 tout_st = HCHK_STATUS_L7TOUT;
4559 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4560 tout_st = HCHK_STATUS_L6TOUT;
4561 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4562 tout_st = HCHK_STATUS_L4TOUT;
4563 else {
4564 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4565 args[cur_arg], args[cur_arg+1]);
4566 goto error;
4567 }
4568 cur_arg++;
4569 }
4570 else {
4571 if (proto == TCPCHK_RULES_HTTP_CHK) {
4572 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004573 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004574 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004575 }
4576 else {
4577 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004578 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4579 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004580 }
4581 goto error;
4582 }
Christopher Faulet39708192020-05-05 10:47:36 +02004583 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004584 cur_arg++;
4585 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004586
Christopher Faulet61cc8522020-04-20 14:54:42 +02004587 chk = calloc(1, sizeof(*chk));
4588 if (!chk) {
4589 memprintf(errmsg, "out of memory");
4590 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004591 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004592 chk->action = TCPCHK_ACT_EXPECT;
4593 LIST_INIT(&chk->expect.onerror_fmt);
4594 LIST_INIT(&chk->expect.onsuccess_fmt);
4595 chk->comment = comment; comment = NULL;
4596 chk->expect.type = type;
4597 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004598 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004599 chk->expect.ok_status = ok_st;
4600 chk->expect.err_status = err_st;
4601 chk->expect.tout_status = tout_st;
4602 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004603
Christopher Faulet61cc8522020-04-20 14:54:42 +02004604 if (on_success_msg) {
4605 px->conf.args.ctx = ARGC_SRV;
4606 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4607 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4608 goto error;
4609 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004610 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004611 if (on_error_msg) {
4612 px->conf.args.ctx = ARGC_SRV;
4613 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4614 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4615 goto error;
4616 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004617 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004618
Christopher Faulet61cc8522020-04-20 14:54:42 +02004619 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004620 case TCPCHK_EXPECT_HTTP_STATUS: {
4621 const char *p = pattern;
4622 unsigned int c1,c2;
4623
4624 chk->expect.codes.codes = NULL;
4625 chk->expect.codes.num = 0;
4626 while (1) {
4627 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4628 if (*p == '-') {
4629 p++;
4630 c2 = read_uint(&p, pattern + strlen(pattern));
4631 }
4632 if (c1 > c2) {
4633 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4634 goto error;
4635 }
4636
4637 chk->expect.codes.num++;
4638 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4639 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4640 if (!chk->expect.codes.codes) {
4641 memprintf(errmsg, "out of memory");
4642 goto error;
4643 }
4644 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4645 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4646
4647 if (*p == '\0')
4648 break;
4649 if (*p != ',') {
4650 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4651 goto error;
4652 }
4653 p++;
4654 }
4655 break;
4656 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004657 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004658 case TCPCHK_EXPECT_HTTP_BODY:
4659 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004660 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004661 memprintf(errmsg, "out of memory");
4662 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004663 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004664 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004665 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004666 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004667
4668 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004669 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4670 goto error;
4671 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004672 chk->expect.data.len = len;
4673 break;
4674 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004675 case TCPCHK_EXPECT_STRING_REGEX:
4676 case TCPCHK_EXPECT_BINARY_REGEX:
4677 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4678 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004679 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004680 if (!chk->expect.regex)
4681 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004682 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004683
4684 case TCPCHK_EXPECT_STRING_LF:
4685 case TCPCHK_EXPECT_BINARY_LF:
4686 case TCPCHK_EXPECT_HTTP_BODY_LF:
4687 LIST_INIT(&chk->expect.fmt);
4688 px->conf.args.ctx = ARGC_SRV;
4689 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4690 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4691 goto error;
4692 }
4693 break;
4694
Christopher Faulet39708192020-05-05 10:47:36 +02004695 case TCPCHK_EXPECT_HTTP_HEADER:
4696 if (!npat) {
4697 memprintf(errmsg, "unexpected error, undefined header name pattern");
4698 goto error;
4699 }
4700 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4701 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4702 if (!chk->expect.hdr.name_re)
4703 goto error;
4704 }
4705 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4706 px->conf.args.ctx = ARGC_SRV;
4707 LIST_INIT(&chk->expect.hdr.name_fmt);
4708 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4709 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4710 goto error;
4711 }
4712 }
4713 else {
4714 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4715 if (!isttest(chk->expect.hdr.name)) {
4716 memprintf(errmsg, "out of memory");
4717 goto error;
4718 }
4719 }
4720
4721 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4722 chk->expect.hdr.value = IST_NULL;
4723 break;
4724 }
4725
4726 if (!vpat) {
4727 memprintf(errmsg, "unexpected error, undefined header value pattern");
4728 goto error;
4729 }
4730 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4731 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4732 if (!chk->expect.hdr.value_re)
4733 goto error;
4734 }
4735 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4736 px->conf.args.ctx = ARGC_SRV;
4737 LIST_INIT(&chk->expect.hdr.value_fmt);
4738 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4739 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4740 goto error;
4741 }
4742 }
4743 else {
4744 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4745 if (!isttest(chk->expect.hdr.value)) {
4746 memprintf(errmsg, "out of memory");
4747 goto error;
4748 }
4749 }
4750
Christopher Faulet61cc8522020-04-20 14:54:42 +02004751 break;
4752 case TCPCHK_EXPECT_CUSTOM:
4753 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4754 break;
4755 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004756 memprintf(errmsg, "pattern not found");
4757 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004758 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004759
Christopher Faulet61cc8522020-04-20 14:54:42 +02004760 /* All tcp-check expect points back to the first inverse expect rule in
4761 * a chain of one or more expect rule, potentially itself.
4762 */
4763 chk->expect.head = chk;
4764 list_for_each_entry_rev(prev_check, rules, list) {
4765 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4766 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4767 chk->expect.head = prev_check;
4768 continue;
4769 }
4770 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4771 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004772 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004773 return chk;
4774
4775 error:
4776 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004777 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004778 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004779 return NULL;
4780}
4781
Christopher Faulet61cc8522020-04-20 14:54:42 +02004782/* Overwrites fields of the old http send rule with those of the new one. When
4783 * replaced, old values are freed and replaced by the new ones. New values are
4784 * not copied but transferred. At the end <new> should be empty and can be
4785 * safely released. This function never fails.
4786 */
4787static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004788{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004789 struct logformat_node *lf, *lfb;
4790 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004791
Christopher Faulet404f9192020-04-09 23:13:54 +02004792
Christopher Faulet61cc8522020-04-20 14:54:42 +02004793 if (new->send.http.meth.str.area) {
4794 free(old->send.http.meth.str.area);
4795 old->send.http.meth.meth = new->send.http.meth.meth;
4796 old->send.http.meth.str.area = new->send.http.meth.str.area;
4797 old->send.http.meth.str.data = new->send.http.meth.str.data;
4798 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004799 }
4800
Christopher Faulet61cc8522020-04-20 14:54:42 +02004801 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4802 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004803 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004804 else
4805 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4806 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4807 old->send.http.uri = new->send.http.uri;
4808 new->send.http.uri = IST_NULL;
4809 }
4810 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4811 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004812 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004813 else
4814 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4815 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4816 LIST_INIT(&old->send.http.uri_fmt);
4817 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4818 LIST_DEL(&lf->list);
4819 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4820 }
4821 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004822
Christopher Faulet61cc8522020-04-20 14:54:42 +02004823 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004824 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004825 old->send.http.vsn = new->send.http.vsn;
4826 new->send.http.vsn = IST_NULL;
4827 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004828
Christopher Faulet61cc8522020-04-20 14:54:42 +02004829 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4830 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4831 LIST_DEL(&hdr->list);
4832 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004833 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004834
4835 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4836 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004837 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004838 else
4839 free_tcpcheck_fmt(&old->send.http.body_fmt);
4840 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4841 old->send.http.body = new->send.http.body;
4842 new->send.http.body = IST_NULL;
4843 }
4844 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4845 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004846 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004847 else
4848 free_tcpcheck_fmt(&old->send.http.body_fmt);
4849 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4850 LIST_INIT(&old->send.http.body_fmt);
4851 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4852 LIST_DEL(&lf->list);
4853 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4854 }
4855 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004856}
4857
Christopher Faulet61cc8522020-04-20 14:54:42 +02004858/* Internal function used to add an http-check rule in a list during the config
4859 * parsing step. Depending on its type, and the previously inserted rules, a
4860 * specific action may be performed or an error may be reported. This functions
4861 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4862 * message.
4863 */
4864static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004865{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004866 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004867
Christopher Faulet61cc8522020-04-20 14:54:42 +02004868 /* the implicit send rule coming from an "option httpchk" line must be
4869 * merged with the first explici http-check send rule, if
4870 * any. Depdending the declaration order some tests are required.
4871 *
4872 * Some tests is also required for other kinds of http-check rules to be
4873 * sure the ruleset remains valid.
4874 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004875
Christopher Faulet61cc8522020-04-20 14:54:42 +02004876 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4877 /* Tries to add an implcit http-check send rule from an "option httpchk" line.
4878 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4879 * following tests are performed :
4880 *
4881 * 1- If there is no such rule or if it is not a send rule, the implicit send
4882 * rule is pushed in front of the ruleset
4883 *
4884 * 2- If it is another implicit send rule, it is replaced with the new one.
4885 *
4886 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
4887 * both, overwritting the old send rule (the explicit one) with info of the
4888 * new send rule (the implicit one).
4889 */
4890 r = get_first_tcpcheck_rule(rules);
4891 if (r && r->action == TCPCHK_ACT_CONNECT)
4892 r = get_next_tcpcheck_rule(rules, r);
4893 if (!r || r->action != TCPCHK_ACT_SEND)
4894 LIST_ADD(rules->list, &chk->list);
4895 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4896 LIST_DEL(&r->list);
4897 free_tcpcheck(r, 0);
4898 LIST_ADD(rules->list, &chk->list);
4899 }
4900 else {
4901 tcpcheck_overwrite_send_http_rule(r, chk);
4902 free_tcpcheck(chk, 0);
4903 }
4904 }
4905 else {
4906 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4907 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4908 * with an existing implicit send rule, if any. At the end, if there is no error,
4909 * the rule is appended to the list.
4910 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004911
Christopher Faulet61cc8522020-04-20 14:54:42 +02004912 r = get_last_tcpcheck_rule(rules);
4913 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4914 /* no error */;
4915 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4916 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4917 chk->index+1);
4918 return 0;
4919 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004920 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004921 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4922 chk->index+1);
4923 return 0;
4924 }
4925 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4926 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4927 chk->index+1);
4928 return 0;
4929 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004930
Christopher Faulet61cc8522020-04-20 14:54:42 +02004931 if (chk->action == TCPCHK_ACT_SEND) {
4932 r = get_first_tcpcheck_rule(rules);
4933 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4934 tcpcheck_overwrite_send_http_rule(r, chk);
4935 free_tcpcheck(chk, 0);
4936 LIST_DEL(&r->list);
4937 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4938 chk = r;
4939 }
4940 }
4941 LIST_ADDQ(rules->list, &chk->list);
4942 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004943 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004944}
4945
Christopher Faulet61cc8522020-04-20 14:54:42 +02004946/**************************************************************************/
4947/************************** Init/deinit checks ****************************/
4948/**************************************************************************/
4949static const char *init_check(struct check *check, int type)
4950{
4951 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004952
Christopher Faulet61cc8522020-04-20 14:54:42 +02004953 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4954 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004955
Christopher Faulet61cc8522020-04-20 14:54:42 +02004956 check->bi.area = calloc(check->bi.size, sizeof(char));
4957 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004958
Christopher Faulet61cc8522020-04-20 14:54:42 +02004959 if (!check->bi.area || !check->bo.area)
4960 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004961
Christopher Faulet61cc8522020-04-20 14:54:42 +02004962 check->wait_list.tasklet = tasklet_new();
4963 if (!check->wait_list.tasklet)
4964 return "out of memory while allocating check tasklet";
4965 check->wait_list.events = 0;
4966 check->wait_list.tasklet->process = event_srv_chk_io;
4967 check->wait_list.tasklet->context = check;
4968 return NULL;
4969}
4970
4971void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004972{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004973 task_destroy(check->task);
4974 if (check->wait_list.tasklet)
4975 tasklet_free(check->wait_list.tasklet);
4976
4977 free(check->bi.area);
4978 free(check->bo.area);
4979 if (check->cs) {
4980 free(check->cs->conn);
4981 check->cs->conn = NULL;
4982 cs_free(check->cs);
4983 check->cs = NULL;
4984 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004985}
4986
Christopher Faulet61cc8522020-04-20 14:54:42 +02004987/* manages a server health-check. Returns the time the task accepts to wait, or
4988 * TIME_ETERNITY for infinity.
4989 */
4990static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004991{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004992 struct check *check = context;
4993
4994 if (check->type == PR_O2_EXT_CHK)
4995 return process_chk_proc(t, context, state);
4996 return process_chk_conn(t, context, state);
4997
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004998}
4999
Christopher Faulet61cc8522020-04-20 14:54:42 +02005000
5001static int start_check_task(struct check *check, int mininter,
5002 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005003{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005004 struct task *t;
5005 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005006
Christopher Faulet61cc8522020-04-20 14:54:42 +02005007 if (check->type == PR_O2_EXT_CHK)
5008 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005009
Christopher Faulet61cc8522020-04-20 14:54:42 +02005010 /* task for the check */
5011 if ((t = task_new(thread_mask)) == NULL) {
5012 ha_alert("Starting [%s:%s] check: out of memory.\n",
5013 check->server->proxy->id, check->server->id);
5014 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005015 }
5016
Christopher Faulet61cc8522020-04-20 14:54:42 +02005017 check->task = t;
5018 t->process = process_chk;
5019 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005020
Christopher Faulet61cc8522020-04-20 14:54:42 +02005021 if (mininter < srv_getinter(check))
5022 mininter = srv_getinter(check);
5023
5024 if (global.max_spread_checks && mininter > global.max_spread_checks)
5025 mininter = global.max_spread_checks;
5026
5027 /* check this every ms */
5028 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5029 check->start = now;
5030 task_queue(t);
5031
5032 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005033}
5034
Christopher Faulet61cc8522020-04-20 14:54:42 +02005035/* updates the server's weight during a warmup stage. Once the final weight is
5036 * reached, the task automatically stops. Note that any server status change
5037 * must have updated s->last_change accordingly.
5038 */
5039static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005040{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005041 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005042
Christopher Faulet61cc8522020-04-20 14:54:42 +02005043 /* by default, plan on stopping the task */
5044 t->expire = TICK_ETERNITY;
5045 if ((s->next_admin & SRV_ADMF_MAINT) ||
5046 (s->next_state != SRV_ST_STARTING))
5047 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005048
Christopher Faulet61cc8522020-04-20 14:54:42 +02005049 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005050
Christopher Faulet61cc8522020-04-20 14:54:42 +02005051 /* recalculate the weights and update the state */
5052 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005053
Christopher Faulet61cc8522020-04-20 14:54:42 +02005054 /* probably that we can refill this server with a bit more connections */
5055 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005056
Christopher Faulet61cc8522020-04-20 14:54:42 +02005057 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005058
Christopher Faulet61cc8522020-04-20 14:54:42 +02005059 /* get back there in 1 second or 1/20th of the slowstart interval,
5060 * whichever is greater, resulting in small 5% steps.
5061 */
5062 if (s->next_state == SRV_ST_STARTING)
5063 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5064 return t;
5065}
5066
5067/*
5068 * Start health-check.
5069 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5070 */
5071static int start_checks()
5072{
5073
5074 struct proxy *px;
5075 struct server *s;
5076 struct task *t;
5077 int nbcheck=0, mininter=0, srvpos=0;
5078
5079 /* 0- init the dummy frontend used to create all checks sessions */
5080 init_new_proxy(&checks_fe);
5081 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5082 checks_fe.mode = PR_MODE_TCP;
5083 checks_fe.maxconn = 0;
5084 checks_fe.conn_retries = CONN_RETRIES;
5085 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5086 checks_fe.timeout.client = TICK_ETERNITY;
5087
5088 /* 1- count the checkers to run simultaneously.
5089 * We also determine the minimum interval among all of those which
5090 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5091 * will be used to spread their start-up date. Those which have
5092 * a shorter interval will start independently and will not dictate
5093 * too short an interval for all others.
5094 */
5095 for (px = proxies_list; px; px = px->next) {
5096 for (s = px->srv; s; s = s->next) {
5097 if (s->slowstart) {
5098 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5099 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5100 return ERR_ALERT | ERR_FATAL;
5101 }
5102 /* We need a warmup task that will be called when the server
5103 * state switches from down to up.
5104 */
5105 s->warmup = t;
5106 t->process = server_warmup;
5107 t->context = s;
5108 /* server can be in this state only because of */
5109 if (s->next_state == SRV_ST_STARTING)
5110 task_schedule(s->warmup, tick_add(now_ms, MS_TO_TICKS(MAX(1000, (now.tv_sec - s->last_change)) / 20)));
Christopher Faulet5c288742020-03-31 08:15:58 +02005111 }
5112
Christopher Faulet61cc8522020-04-20 14:54:42 +02005113 if (s->check.state & CHK_ST_CONFIGURED) {
5114 nbcheck++;
5115 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5116 (!mininter || mininter > srv_getinter(&s->check)))
5117 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005118 }
5119
Christopher Faulet61cc8522020-04-20 14:54:42 +02005120 if (s->agent.state & CHK_ST_CONFIGURED) {
5121 nbcheck++;
5122 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5123 (!mininter || mininter > srv_getinter(&s->agent)))
5124 mininter = srv_getinter(&s->agent);
5125 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005126 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005127 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005128
Christopher Faulet61cc8522020-04-20 14:54:42 +02005129 if (!nbcheck)
5130 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005131
Christopher Faulet61cc8522020-04-20 14:54:42 +02005132 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005133
Christopher Faulet61cc8522020-04-20 14:54:42 +02005134 /*
5135 * 2- start them as far as possible from each others. For this, we will
5136 * start them after their interval set to the min interval divided by
5137 * the number of servers, weighted by the server's position in the list.
5138 */
5139 for (px = proxies_list; px; px = px->next) {
5140 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5141 if (init_pid_list()) {
5142 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5143 return ERR_ALERT | ERR_FATAL;
5144 }
5145 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005146
Christopher Faulet61cc8522020-04-20 14:54:42 +02005147 for (s = px->srv; s; s = s->next) {
5148 /* A task for the main check */
5149 if (s->check.state & CHK_ST_CONFIGURED) {
5150 if (s->check.type == PR_O2_EXT_CHK) {
5151 if (!prepare_external_check(&s->check))
5152 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005153 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005154 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5155 return ERR_ALERT | ERR_FATAL;
5156 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005157 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005158
Christopher Faulet61cc8522020-04-20 14:54:42 +02005159 /* A task for a auxiliary agent check */
5160 if (s->agent.state & CHK_ST_CONFIGURED) {
5161 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5162 return ERR_ALERT | ERR_FATAL;
5163 }
5164 srvpos++;
5165 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005166 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005167 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005168 return 0;
5169}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005170
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005171
Christopher Faulet61cc8522020-04-20 14:54:42 +02005172/*
5173 * Return value:
5174 * the port to be used for the health check
5175 * 0 in case no port could be found for the check
5176 */
5177static int srv_check_healthcheck_port(struct check *chk)
5178{
5179 int i = 0;
5180 struct server *srv = NULL;
5181
5182 srv = chk->server;
5183
5184 /* by default, we use the health check port ocnfigured */
5185 if (chk->port > 0)
5186 return chk->port;
5187
5188 /* try to get the port from check_core.addr if check.port not set */
5189 i = get_host_port(&chk->addr);
5190 if (i > 0)
5191 return i;
5192
5193 /* try to get the port from server address */
5194 /* prevent MAPPORTS from working at this point, since checks could
5195 * not be performed in such case (MAPPORTS impose a relative ports
5196 * based on live traffic)
5197 */
5198 if (srv->flags & SRV_F_MAPPORTS)
5199 return 0;
5200
5201 i = srv->svc_port; /* by default */
5202 if (i > 0)
5203 return i;
5204
5205 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005206}
5207
Christopher Faulet61cc8522020-04-20 14:54:42 +02005208/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5209 * if an error occurred.
5210 */
5211static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005212{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005213 const char *err;
5214 struct tcpcheck_rule *r;
5215 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005216
Christopher Faulet61cc8522020-04-20 14:54:42 +02005217 if (!srv->do_check)
5218 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005219
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005220
Christopher Faulet61cc8522020-04-20 14:54:42 +02005221 /* If neither a port nor an addr was specified and no check transport
5222 * layer is forced, then the transport layer used by the checks is the
5223 * same as for the production traffic. Otherwise we use raw_sock by
5224 * default, unless one is specified.
5225 */
5226 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5227 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5228 srv->check.use_ssl = srv->use_ssl;
5229 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005230 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005231 else if (srv->check.use_ssl == 1)
5232 srv->check.xprt = xprt_get(XPRT_SSL);
5233 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005234 }
5235
Christopher Faulet12882cf2020-04-23 15:50:18 +02005236 /* Inherit the mux protocol from the server if not already defined for
5237 * the check
5238 */
5239 if (srv->mux_proto && !srv->check.mux_proto)
5240 srv->check.mux_proto = srv->mux_proto;
5241
Christopher Faulet61cc8522020-04-20 14:54:42 +02005242 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005243
Christopher Faulet61cc8522020-04-20 14:54:42 +02005244 /* We need at least a service port, a check port or the first tcp-check
5245 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5246 */
5247 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5248 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5249 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005250
Christopher Faulet61cc8522020-04-20 14:54:42 +02005251 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5252 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5253 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5254 ret |= ERR_ALERT | ERR_ABORT;
5255 goto out;
5256 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005257
Christopher Faulet61cc8522020-04-20 14:54:42 +02005258 /* search the first action (connect / send / expect) in the list */
5259 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5260 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5261 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5262 "nor tcp_check rule 'connect' with port information.\n",
5263 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5264 ret |= ERR_ALERT | ERR_ABORT;
5265 goto out;
5266 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005267
Christopher Faulet61cc8522020-04-20 14:54:42 +02005268 /* scan the tcp-check ruleset to ensure a port has been configured */
5269 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5270 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5271 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5272 "and a tcp_check rule 'connect' with no port information.\n",
5273 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5274 ret |= ERR_ALERT | ERR_ABORT;
5275 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005276 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005277 }
5278
Christopher Faulet61cc8522020-04-20 14:54:42 +02005279 init:
5280 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5281 struct tcpcheck_ruleset *rs = NULL;
5282 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5283 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005284
Christopher Faulet61cc8522020-04-20 14:54:42 +02005285 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5286 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005287
Christopher Faulet61cc8522020-04-20 14:54:42 +02005288 rs = find_tcpcheck_ruleset("*tcp-check");
5289 if (!rs) {
5290 rs = create_tcpcheck_ruleset("*tcp-check");
5291 if (rs == NULL) {
5292 ha_alert("config: %s '%s': out of memory.\n",
5293 proxy_type_str(srv->proxy), srv->proxy->id);
5294 ret |= ERR_ALERT | ERR_FATAL;
5295 goto out;
5296 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005297 }
5298
Christopher Faulet61cc8522020-04-20 14:54:42 +02005299 free_tcpcheck_vars(&rules->preset_vars);
5300 rules->list = &rs->rules;
5301 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005302 }
5303
Christopher Faulet61cc8522020-04-20 14:54:42 +02005304 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5305 if (err) {
5306 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5307 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5308 ret |= ERR_ALERT | ERR_ABORT;
5309 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005310 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005311 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5312 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005313
Christopher Faulet61cc8522020-04-20 14:54:42 +02005314 out:
5315 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005316}
5317
Christopher Faulet61cc8522020-04-20 14:54:42 +02005318/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5319 * if an error occurred.
5320 */
5321static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005322{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005323 struct tcpcheck_rule *chk;
5324 const char *err;
5325 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005326
Christopher Faulet61cc8522020-04-20 14:54:42 +02005327 if (!srv->do_agent)
5328 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005329
Christopher Faulet61cc8522020-04-20 14:54:42 +02005330 /* If there is no connect rule preceeding all send / expect rules, an
5331 * implicit one is inserted before all others.
5332 */
5333 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5334 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5335 chk = calloc(1, sizeof(*chk));
5336 if (!chk) {
5337 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5338 " to agent-check for server '%s' (out of memory).\n",
5339 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5340 ret |= ERR_ALERT | ERR_FATAL;
5341 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005342 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005343 chk->action = TCPCHK_ACT_CONNECT;
5344 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5345 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005346 }
5347
Christopher Faulete5870d82020-04-15 11:32:03 +02005348
Christopher Faulet61cc8522020-04-20 14:54:42 +02005349 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5350 if (err) {
5351 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5352 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5353 ret |= ERR_ALERT | ERR_ABORT;
5354 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005355 }
5356
Christopher Faulet61cc8522020-04-20 14:54:42 +02005357 if (!srv->agent.inter)
5358 srv->agent.inter = srv->check.inter;
5359
5360 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5361 global.maxsock++;
5362
5363 out:
5364 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005365}
5366
Christopher Faulet61cc8522020-04-20 14:54:42 +02005367/* Check tcp-check health-check configuration for the proxy <px>. */
5368static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005369{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005370 struct tcpcheck_rule *chk, *back;
5371 char *comment = NULL, *errmsg = NULL;
5372 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5373 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005374
Christopher Faulet61cc8522020-04-20 14:54:42 +02005375 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5376 deinit_proxy_tcpcheck(px);
5377 goto out;
5378 }
5379
5380 free(px->check_command);
5381 free(px->check_path);
5382 px->check_command = px->check_path = NULL;
5383
5384 if (!px->tcpcheck_rules.list) {
5385 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5386 ret |= ERR_ALERT | ERR_FATAL;
5387 goto out;
5388 }
5389
5390 /* HTTP ruleset only : */
5391 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5392 struct tcpcheck_rule *next;
5393
5394 /* move remaining implicit send rule from "option httpchk" line to the right place.
5395 * If such rule exists, it must be the first one. In this case, the rule is moved
5396 * after the first connect rule, if any. Otherwise, nothing is done.
5397 */
5398 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5399 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5400 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5401 if (next && next->action == TCPCHK_ACT_CONNECT) {
5402 LIST_DEL(&chk->list);
5403 LIST_ADD(&next->list, &chk->list);
5404 chk->index = next->index;
5405 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005406 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005407
5408 /* add implicit expect rule if the last one is a send. It is inherited from previous
5409 * versions where the http expect rule was optional. Now it is possible to chained
5410 * send/expect rules but the last expect may still be implicit.
5411 */
5412 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5413 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005414 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005415 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5416 px->conf.file, px->conf.line, &errmsg);
5417 if (!next) {
5418 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5419 "(%s).\n", px->id, errmsg);
5420 free(errmsg);
5421 ret |= ERR_ALERT | ERR_FATAL;
5422 goto out;
5423 }
5424 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5425 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005426 }
5427 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005428
5429 /* For all ruleset: */
5430
5431 /* If there is no connect rule preceeding all send / expect rules, an
5432 * implicit one is inserted before all others.
5433 */
5434 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5435 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5436 chk = calloc(1, sizeof(*chk));
5437 if (!chk) {
5438 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5439 "(out of memory).\n", px->id);
5440 ret |= ERR_ALERT | ERR_FATAL;
5441 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005442 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005443 chk->action = TCPCHK_ACT_CONNECT;
5444 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5445 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5446 }
5447
5448 /* Remove all comment rules. To do so, when a such rule is found, the
5449 * comment is assigned to the following rule(s).
5450 */
5451 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5452 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5453 free(comment);
5454 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005455 }
5456
Christopher Faulet61cc8522020-04-20 14:54:42 +02005457 prev_action = chk->action;
5458 switch (chk->action) {
5459 case TCPCHK_ACT_COMMENT:
5460 free(comment);
5461 comment = chk->comment;
5462 LIST_DEL(&chk->list);
5463 free(chk);
5464 break;
5465 case TCPCHK_ACT_CONNECT:
5466 if (!chk->comment && comment)
5467 chk->comment = strdup(comment);
5468 /* fall though */
5469 case TCPCHK_ACT_ACTION_KW:
5470 free(comment);
5471 comment = NULL;
5472 break;
5473 case TCPCHK_ACT_SEND:
5474 case TCPCHK_ACT_EXPECT:
5475 if (!chk->comment && comment)
5476 chk->comment = strdup(comment);
5477 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005478 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005479 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005480 free(comment);
5481 comment = NULL;
5482
5483 out:
5484 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005485}
5486
Christopher Faulet61cc8522020-04-20 14:54:42 +02005487void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005488{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005489 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5490 px->tcpcheck_rules.flags = 0;
5491 px->tcpcheck_rules.list = NULL;
5492}
Christopher Faulete5870d82020-04-15 11:32:03 +02005493
Christopher Faulet61cc8522020-04-20 14:54:42 +02005494static void deinit_srv_check(struct server *srv)
5495{
5496 if (srv->check.state & CHK_ST_CONFIGURED)
5497 free_check(&srv->check);
5498 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5499 srv->do_check = 0;
5500}
Christopher Faulete5870d82020-04-15 11:32:03 +02005501
Christopher Faulet61cc8522020-04-20 14:54:42 +02005502
5503static void deinit_srv_agent_check(struct server *srv)
5504{
5505 if (srv->agent.tcpcheck_rules) {
5506 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5507 free(srv->agent.tcpcheck_rules);
5508 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005509 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005510
Christopher Faulet61cc8522020-04-20 14:54:42 +02005511 if (srv->agent.state & CHK_ST_CONFIGURED)
5512 free_check(&srv->agent);
5513
5514 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5515 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005516}
5517
Christopher Faulet61cc8522020-04-20 14:54:42 +02005518static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005519{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005520 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005521 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005522 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005523
Christopher Fauletd7cee712020-04-21 13:45:00 +02005524 node = ebpt_first(&shared_tcpchecks);
5525 while (node) {
5526 next = ebpt_next(node);
5527 ebpt_delete(node);
5528 free(node->key);
5529 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005530 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5531 LIST_DEL(&r->list);
5532 free_tcpcheck(r, 0);
5533 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005534 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005535 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005536 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005537}
Christopher Faulete5870d82020-04-15 11:32:03 +02005538
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005539
Christopher Faulet61cc8522020-04-20 14:54:42 +02005540REGISTER_POST_SERVER_CHECK(init_srv_check);
5541REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5542REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5543REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005544
Christopher Faulet61cc8522020-04-20 14:54:42 +02005545REGISTER_SERVER_DEINIT(deinit_srv_check);
5546REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5547REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5548REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005549
Christopher Faulet61cc8522020-04-20 14:54:42 +02005550/**************************************************************************/
5551/****************************** Email alerts ******************************/
5552/* NOTE: It may be pertinent to use an applet to handle email alerts */
5553/* instead of a tcp-check ruleset */
5554/**************************************************************************/
5555void email_alert_free(struct email_alert *alert)
5556{
5557 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005558
Christopher Faulet61cc8522020-04-20 14:54:42 +02005559 if (!alert)
5560 return;
5561
5562 if (alert->rules.list) {
5563 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5564 LIST_DEL(&rule->list);
5565 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005566 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005567 free_tcpcheck_vars(&alert->rules.preset_vars);
5568 free(alert->rules.list);
5569 alert->rules.list = NULL;
5570 }
5571 pool_free(pool_head_email_alert, alert);
5572}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005573
Christopher Faulet61cc8522020-04-20 14:54:42 +02005574static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5575{
5576 struct check *check = context;
5577 struct email_alertq *q;
5578 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005579
Christopher Faulet61cc8522020-04-20 14:54:42 +02005580 q = container_of(check, typeof(*q), check);
5581
5582 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5583 while (1) {
5584 if (!(check->state & CHK_ST_ENABLED)) {
5585 if (LIST_ISEMPTY(&q->email_alerts)) {
5586 /* All alerts processed, queue the task */
5587 t->expire = TICK_ETERNITY;
5588 task_queue(t);
5589 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005590 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005591
5592 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5593 LIST_DEL(&alert->list);
5594 t->expire = now_ms;
5595 check->tcpcheck_rules = &alert->rules;
5596 check->status = HCHK_STATUS_INI;
5597 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005598 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005599
5600 process_chk(t, context, state);
5601 if (check->state & CHK_ST_INPROGRESS)
5602 break;
5603
5604 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5605 email_alert_free(alert);
5606 check->tcpcheck_rules = NULL;
5607 check->server = NULL;
5608 check->state &= ~CHK_ST_ENABLED;
5609 }
5610 end:
5611 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5612 return t;
5613}
5614
5615/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5616 *
5617 * The function returns 1 in success case, otherwise, it returns 0 and err is
5618 * filled.
5619 */
5620int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5621{
5622 struct mailer *mailer;
5623 struct email_alertq *queues;
5624 const char *err_str;
5625 int i = 0;
5626
5627 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5628 memprintf(err, "out of memory while allocating mailer alerts queues");
5629 goto fail_no_queue;
5630 }
5631
5632 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5633 struct email_alertq *q = &queues[i];
5634 struct check *check = &q->check;
5635 struct task *t;
5636
5637 LIST_INIT(&q->email_alerts);
5638 HA_SPIN_INIT(&q->lock);
5639 check->inter = mls->timeout.mail;
5640 check->rise = DEF_AGENT_RISETIME;
5641 check->proxy = p;
5642 check->fall = DEF_AGENT_FALLTIME;
5643 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5644 memprintf(err, "%s", err_str);
5645 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005646 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005647
5648 check->xprt = mailer->xprt;
5649 check->addr = mailer->addr;
5650 check->port = get_host_port(&mailer->addr);
5651
5652 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5653 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005654 goto error;
5655 }
5656
Christopher Faulet61cc8522020-04-20 14:54:42 +02005657 check->task = t;
5658 t->process = process_email_alert;
5659 t->context = check;
5660
5661 /* check this in one ms */
5662 t->expire = TICK_ETERNITY;
5663 check->start = now;
5664 task_queue(t);
5665 }
5666
5667 mls->users++;
5668 free(p->email_alert.mailers.name);
5669 p->email_alert.mailers.m = mls;
5670 p->email_alert.queues = queues;
5671 return 0;
5672
5673 error:
5674 for (i = 0; i < mls->count; i++) {
5675 struct email_alertq *q = &queues[i];
5676 struct check *check = &q->check;
5677
5678 free_check(check);
5679 }
5680 free(queues);
5681 fail_no_queue:
5682 return 1;
5683}
5684
5685static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5686{
5687 struct tcpcheck_rule *tcpcheck, *prev_check;
5688 struct tcpcheck_expect *expect;
5689
5690 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5691 return 0;
5692 memset(tcpcheck, 0, sizeof(*tcpcheck));
5693 tcpcheck->action = TCPCHK_ACT_EXPECT;
5694
5695 expect = &tcpcheck->expect;
5696 expect->type = TCPCHK_EXPECT_STRING;
5697 LIST_INIT(&expect->onerror_fmt);
5698 LIST_INIT(&expect->onsuccess_fmt);
5699 expect->ok_status = HCHK_STATUS_L7OKD;
5700 expect->err_status = HCHK_STATUS_L7RSP;
5701 expect->tout_status = HCHK_STATUS_L7TOUT;
5702 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005703 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005704 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5705 return 0;
5706 }
5707
5708 /* All tcp-check expect points back to the first inverse expect rule
5709 * in a chain of one or more expect rule, potentially itself.
5710 */
5711 tcpcheck->expect.head = tcpcheck;
5712 list_for_each_entry_rev(prev_check, rules->list, list) {
5713 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5714 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5715 tcpcheck->expect.head = prev_check;
5716 continue;
5717 }
5718 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5719 break;
5720 }
5721 LIST_ADDQ(rules->list, &tcpcheck->list);
5722 return 1;
5723}
5724
5725static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5726{
5727 struct tcpcheck_rule *tcpcheck;
5728 struct tcpcheck_send *send;
5729 const char *in;
5730 char *dst;
5731 int i;
5732
5733 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5734 return 0;
5735 memset(tcpcheck, 0, sizeof(*tcpcheck));
5736 tcpcheck->action = TCPCHK_ACT_SEND;
5737
5738 send = &tcpcheck->send;
5739 send->type = TCPCHK_SEND_STRING;
5740
5741 for (i = 0; strs[i]; i++)
5742 send->data.len += strlen(strs[i]);
5743
Christopher Fauletb61caf42020-04-21 10:57:42 +02005744 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005745 if (!isttest(send->data)) {
5746 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5747 return 0;
5748 }
5749
Christopher Fauletb61caf42020-04-21 10:57:42 +02005750 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005751 for (i = 0; strs[i]; i++)
5752 for (in = strs[i]; (*dst = *in++); dst++);
5753 *dst = 0;
5754
5755 LIST_ADDQ(rules->list, &tcpcheck->list);
5756 return 1;
5757}
5758
5759static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5760 struct email_alertq *q, const char *msg)
5761{
5762 struct email_alert *alert;
5763 struct tcpcheck_rule *tcpcheck;
5764 struct check *check = &q->check;
5765
5766 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5767 goto error;
5768 LIST_INIT(&alert->list);
5769 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5770 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5771 if (!alert->rules.list)
5772 goto error;
5773 LIST_INIT(alert->rules.list);
5774 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5775 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005776
Christopher Faulet61cc8522020-04-20 14:54:42 +02005777 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5778 goto error;
5779 memset(tcpcheck, 0, sizeof(*tcpcheck));
5780 tcpcheck->action = TCPCHK_ACT_CONNECT;
5781 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005782
Christopher Faulet61cc8522020-04-20 14:54:42 +02005783 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005784
Christopher Faulet61cc8522020-04-20 14:54:42 +02005785 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005786 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005787
5788 {
5789 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5790 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5791 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005792 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005793
Christopher Faulet61cc8522020-04-20 14:54:42 +02005794 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5795 goto error;
5796
5797 {
5798 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5799 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005800 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005801 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005802
5803 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5804 goto error;
5805
5806 {
5807 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5808 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005809 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005810 }
5811
Christopher Faulet61cc8522020-04-20 14:54:42 +02005812 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5813 goto error;
5814
5815 {
5816 const char * const strs[2] = { "DATA\r\n" };
5817 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005818 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005819 }
5820
5821 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5822 goto error;
5823
5824 {
5825 struct tm tm;
5826 char datestr[48];
5827 const char * const strs[18] = {
5828 "From: ", p->email_alert.from, "\r\n",
5829 "To: ", p->email_alert.to, "\r\n",
5830 "Date: ", datestr, "\r\n",
5831 "Subject: [HAproxy Alert] ", msg, "\r\n",
5832 "\r\n",
5833 msg, "\r\n",
5834 "\r\n",
5835 ".\r\n",
5836 NULL
5837 };
5838
5839 get_localtime(date.tv_sec, &tm);
5840
5841 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005842 goto error;
5843 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005844
5845 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005846 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005847 }
5848
5849 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005850 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005851
5852 {
5853 const char * const strs[2] = { "QUIT\r\n" };
5854 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5855 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005856 }
5857
Christopher Faulet61cc8522020-04-20 14:54:42 +02005858 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5859 goto error;
5860
5861 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5862 task_wakeup(check->task, TASK_WOKEN_MSG);
5863 LIST_ADDQ(&q->email_alerts, &alert->list);
5864 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5865 return 1;
5866
5867error:
5868 email_alert_free(alert);
5869 return 0;
5870}
5871
5872static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5873{
5874 int i;
5875 struct mailer *mailer;
5876
5877 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5878 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5879 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5880 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5881 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005882 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005883 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005884
Christopher Faulet61cc8522020-04-20 14:54:42 +02005885 return;
5886}
5887
5888/*
5889 * Send email alert if configured.
5890 */
5891void send_email_alert(struct server *s, int level, const char *format, ...)
5892{
5893 va_list argp;
5894 char buf[1024];
5895 int len;
5896 struct proxy *p = s->proxy;
5897
5898 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5899 return;
5900
5901 va_start(argp, format);
5902 len = vsnprintf(buf, sizeof(buf), format, argp);
5903 va_end(argp);
5904
5905 if (len < 0 || len >= sizeof(buf)) {
5906 ha_alert("Email alert [%s] could not format message\n", p->id);
5907 return;
5908 }
5909
5910 enqueue_email_alert(p, s, buf);
5911}
5912
5913/**************************************************************************/
5914/************************** Check sample fetches **************************/
5915/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005916
Christopher Faulet61cc8522020-04-20 14:54:42 +02005917static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005918 { /* END */ },
5919}};
5920
5921INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5922
5923
5924/**************************************************************************/
5925/************************ Check's parsing functions ***********************/
5926/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005927/* Parses the "tcp-check" proxy keyword */
5928static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5929 struct proxy *defpx, const char *file, int line,
5930 char **errmsg)
5931{
Christopher Faulet404f9192020-04-09 23:13:54 +02005932 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005933 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005934 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005935
5936 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5937 ret = 1;
5938
Christopher Faulet404f9192020-04-09 23:13:54 +02005939 /* Deduce the ruleset name from the proxy info */
5940 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5941 ((curpx == defpx) ? "defaults" : curpx->id),
5942 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005943
Christopher Faulet61cc8522020-04-20 14:54:42 +02005944 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005945 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005946 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005947 if (rs == NULL) {
5948 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005949 goto error;
5950 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005951 }
5952
Gaetan Rivet5301b012020-02-25 17:19:17 +01005953 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005954 if (!LIST_ISEMPTY(&rs->rules)) {
5955 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005956 index = chk->index + 1;
5957 }
5958
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005959 cur_arg = 1;
5960 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005961 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005962 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5963 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005964 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005965 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005966 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005967 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005968 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005969 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005970 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5971
5972 if (!kw) {
5973 action_kw_tcp_check_build_list(&trash);
5974 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5975 "%s%s. but got '%s'",
5976 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5977 goto error;
5978 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005979 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005980 }
5981
5982 if (!chk) {
5983 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5984 goto error;
5985 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005986 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005987
5988 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005989 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005990 LIST_ADDQ(&rs->rules, &chk->list);
5991
5992 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005993 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005994 /* Use this ruleset if the proxy already has tcp-check enabled */
5995 curpx->tcpcheck_rules.list = &rs->rules;
5996 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5997 }
5998 else {
5999 /* mark this ruleset as unused for now */
6000 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
6001 }
6002
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006003 return ret;
6004
6005 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006006 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006007 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006008 return -1;
6009}
6010
Christopher Faulet51b129f2020-04-09 15:54:18 +02006011/* Parses the "http-check" proxy keyword */
6012static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6013 struct proxy *defpx, const char *file, int line,
6014 char **errmsg)
6015{
Christopher Faulete5870d82020-04-15 11:32:03 +02006016 struct tcpcheck_ruleset *rs = NULL;
6017 struct tcpcheck_rule *chk = NULL;
6018 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006019
6020 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6021 ret = 1;
6022
6023 cur_arg = 1;
6024 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6025 /* enable a graceful server shutdown on an HTTP 404 response */
6026 curpx->options |= PR_O_DISABLE404;
6027 if (too_many_args(1, args, errmsg, NULL))
6028 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006029 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006030 }
6031 else if (strcmp(args[cur_arg], "send-state") == 0) {
6032 /* enable emission of the apparent state of a server in HTTP checks */
6033 curpx->options2 |= PR_O2_CHK_SNDST;
6034 if (too_many_args(1, args, errmsg, NULL))
6035 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006036 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006037 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006038
Christopher Faulete5870d82020-04-15 11:32:03 +02006039 /* Deduce the ruleset name from the proxy info */
6040 chunk_printf(&trash, "*http-check-%s_%s-%d",
6041 ((curpx == defpx) ? "defaults" : curpx->id),
6042 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006043
Christopher Faulet61cc8522020-04-20 14:54:42 +02006044 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006045 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006046 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006047 if (rs == NULL) {
6048 memprintf(errmsg, "out of memory.\n");
6049 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006050 }
6051 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006052
Christopher Faulete5870d82020-04-15 11:32:03 +02006053 index = 0;
6054 if (!LIST_ISEMPTY(&rs->rules)) {
6055 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6056 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6057 index = chk->index + 1;
6058 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006059
Christopher Faulete5870d82020-04-15 11:32:03 +02006060 if (strcmp(args[cur_arg], "connect") == 0)
6061 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6062 else if (strcmp(args[cur_arg], "send") == 0)
6063 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6064 else if (strcmp(args[cur_arg], "expect") == 0)
6065 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6066 file, line, errmsg);
6067 else if (strcmp(args[cur_arg], "comment") == 0)
6068 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6069 else {
6070 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006071
Christopher Faulete5870d82020-04-15 11:32:03 +02006072 if (!kw) {
6073 action_kw_tcp_check_build_list(&trash);
6074 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6075 " 'send', 'expect'%s%s. but got '%s'",
6076 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6077 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006078 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006079 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6080 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006081
Christopher Faulete5870d82020-04-15 11:32:03 +02006082 if (!chk) {
6083 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6084 goto error;
6085 }
6086 ret = (*errmsg != NULL); /* Handle warning */
6087
6088 chk->index = index;
6089 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6090 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6091 /* Use this ruleset if the proxy already has http-check enabled */
6092 curpx->tcpcheck_rules.list = &rs->rules;
6093 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6094 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6095 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6096 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006097 goto error;
6098 }
6099 }
6100 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006101 /* mark this ruleset as unused for now */
6102 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6103 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006104 }
6105
Christopher Faulete5870d82020-04-15 11:32:03 +02006106 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006107 return ret;
6108
6109 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006110 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006111 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006112 return -1;
6113}
6114
Christopher Faulete9111b62020-04-09 18:12:08 +02006115/* Parses the "external-check" proxy keyword */
6116static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6117 struct proxy *defpx, const char *file, int line,
6118 char **errmsg)
6119{
6120 int cur_arg, ret = 0;
6121
6122 cur_arg = 1;
6123 if (!*(args[cur_arg])) {
6124 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6125 goto error;
6126 }
6127
6128 if (strcmp(args[cur_arg], "command") == 0) {
6129 if (too_many_args(2, args, errmsg, NULL))
6130 goto error;
6131 if (!*(args[cur_arg+1])) {
6132 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6133 goto error;
6134 }
6135 free(curpx->check_command);
6136 curpx->check_command = strdup(args[cur_arg+1]);
6137 }
6138 else if (strcmp(args[cur_arg], "path") == 0) {
6139 if (too_many_args(2, args, errmsg, NULL))
6140 goto error;
6141 if (!*(args[cur_arg+1])) {
6142 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6143 goto error;
6144 }
6145 free(curpx->check_path);
6146 curpx->check_path = strdup(args[cur_arg+1]);
6147 }
6148 else {
6149 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6150 args[0], args[1]);
6151 goto error;
6152 }
6153
6154 ret = (*errmsg != NULL); /* Handle warning */
6155 return ret;
6156
6157error:
6158 return -1;
6159}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006160
Christopher Faulet430e4802020-04-09 15:28:16 +02006161/* Parses the "option tcp-check" proxy keyword */
6162int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6163 const char *file, int line)
6164{
Christopher Faulet404f9192020-04-09 23:13:54 +02006165 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006166 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6167 int err_code = 0;
6168
6169 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6170 err_code |= ERR_WARN;
6171
6172 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6173 goto out;
6174
Christopher Faulet404f9192020-04-09 23:13:54 +02006175 curpx->options2 &= ~PR_O2_CHK_ANY;
6176 curpx->options2 |= PR_O2_TCPCHK_CHK;
6177
Christopher Fauletd7e63962020-04-17 20:15:59 +02006178 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006179 /* If a tcp-check rulesset is already set, do nothing */
6180 if (rules->list)
6181 goto out;
6182
6183 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6184 * get it.
6185 */
6186 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6187 goto curpx_ruleset;
6188
6189 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6190 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006191 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006192 if (rs)
6193 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006194 }
6195
Christopher Faulet404f9192020-04-09 23:13:54 +02006196 curpx_ruleset:
6197 /* Deduce the ruleset name from the proxy info */
6198 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6199 ((curpx == defpx) ? "defaults" : curpx->id),
6200 curpx->conf.file, curpx->conf.line);
6201
Christopher Faulet61cc8522020-04-20 14:54:42 +02006202 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006203 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006204 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006205 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006206 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6207 goto error;
6208 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006209 }
6210
Christopher Faulet404f9192020-04-09 23:13:54 +02006211 ruleset_found:
6212 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006213 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006214 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006215
6216 out:
6217 return err_code;
6218
6219 error:
6220 err_code |= ERR_ALERT | ERR_FATAL;
6221 goto out;
6222}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006223
6224/* Parses the "option redis-check" proxy keyword */
6225int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6226 const char *file, int line)
6227{
6228 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6229 static char *redis_res = "+PONG\r\n";
6230
6231 struct tcpcheck_ruleset *rs = NULL;
6232 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6233 struct tcpcheck_rule *chk;
6234 char *errmsg = NULL;
6235 int err_code = 0;
6236
6237 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6238 err_code |= ERR_WARN;
6239
6240 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6241 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006242
6243 curpx->options2 &= ~PR_O2_CHK_ANY;
6244 curpx->options2 |= PR_O2_TCPCHK_CHK;
6245
6246 free_tcpcheck_vars(&rules->preset_vars);
6247 rules->list = NULL;
6248 rules->flags = 0;
6249
Christopher Faulet61cc8522020-04-20 14:54:42 +02006250 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006251 if (rs)
6252 goto ruleset_found;
6253
Christopher Faulet61cc8522020-04-20 14:54:42 +02006254 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006255 if (rs == NULL) {
6256 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6257 goto error;
6258 }
6259
6260 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6261 1, curpx, &rs->rules, file, line, &errmsg);
6262 if (!chk) {
6263 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6264 goto error;
6265 }
6266 chk->index = 0;
6267 LIST_ADDQ(&rs->rules, &chk->list);
6268
6269 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6270 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006271 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006272 "on-success", "Redis server is ok",
6273 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006274 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006275 if (!chk) {
6276 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6277 goto error;
6278 }
6279 chk->index = 1;
6280 LIST_ADDQ(&rs->rules, &chk->list);
6281
Christopher Fauletd7cee712020-04-21 13:45:00 +02006282 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006283
6284 ruleset_found:
6285 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006286 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006287
6288 out:
6289 free(errmsg);
6290 return err_code;
6291
6292 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006293 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006294 err_code |= ERR_ALERT | ERR_FATAL;
6295 goto out;
6296}
6297
Christopher Faulet811f78c2020-04-01 11:10:27 +02006298
6299/* Parses the "option ssl-hello-chk" proxy keyword */
6300int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6301 const char *file, int line)
6302{
6303 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6304 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6305 *
6306 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6307 */
6308 static char sslv3_client_hello[] = {
6309 "16" /* ContentType : 0x16 = Hanshake */
6310 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6311 "0079" /* ContentLength : 0x79 bytes after this one */
6312 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6313 "000075" /* HandshakeLength : 0x75 bytes after this one */
6314 "0300" /* Hello Version : 0x0300 = v3 */
6315 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6316 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6317 "00" /* Session ID length : empty (no session ID) */
6318 "004E" /* Cipher Suite Length : 78 bytes after this one */
6319 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6320 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6321 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6322 "000D" "000E" "000F" "0010" /* various bit lengths, */
6323 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6324 "0015" "0016" "0017" "0018"
6325 "0019" "001A" "001B" "002F"
6326 "0030" "0031" "0032" "0033"
6327 "0034" "0035" "0036" "0037"
6328 "0038" "0039" "003A"
6329 "01" /* Compression Length : 0x01 = 1 byte for types */
6330 "00" /* Compression Type : 0x00 = NULL compression */
6331 };
6332
6333 struct tcpcheck_ruleset *rs = NULL;
6334 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6335 struct tcpcheck_rule *chk;
6336 char *errmsg = NULL;
6337 int err_code = 0;
6338
6339 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6340 err_code |= ERR_WARN;
6341
6342 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6343 goto out;
6344
Christopher Faulet811f78c2020-04-01 11:10:27 +02006345 curpx->options2 &= ~PR_O2_CHK_ANY;
6346 curpx->options2 |= PR_O2_TCPCHK_CHK;
6347
6348 free_tcpcheck_vars(&rules->preset_vars);
6349 rules->list = NULL;
6350 rules->flags = 0;
6351
Christopher Faulet61cc8522020-04-20 14:54:42 +02006352 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006353 if (rs)
6354 goto ruleset_found;
6355
Christopher Faulet61cc8522020-04-20 14:54:42 +02006356 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006357 if (rs == NULL) {
6358 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6359 goto error;
6360 }
6361
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006362 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006363 1, curpx, &rs->rules, file, line, &errmsg);
6364 if (!chk) {
6365 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6366 goto error;
6367 }
6368 chk->index = 0;
6369 LIST_ADDQ(&rs->rules, &chk->list);
6370
6371 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006372 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006373 "error-status", "L6RSP", "tout-status", "L6TOUT",
6374 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006375 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006376 if (!chk) {
6377 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6378 goto error;
6379 }
6380 chk->index = 1;
6381 LIST_ADDQ(&rs->rules, &chk->list);
6382
Christopher Fauletd7cee712020-04-21 13:45:00 +02006383 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006384
6385 ruleset_found:
6386 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006387 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006388
6389 out:
6390 free(errmsg);
6391 return err_code;
6392
6393 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006394 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006395 err_code |= ERR_ALERT | ERR_FATAL;
6396 goto out;
6397}
6398
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006399/* Parses the "option smtpchk" proxy keyword */
6400int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6401 const char *file, int line)
6402{
6403 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6404
6405 struct tcpcheck_ruleset *rs = NULL;
6406 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6407 struct tcpcheck_rule *chk;
6408 struct tcpcheck_var *var = NULL;
6409 char *cmd = NULL, *errmsg = NULL;
6410 int err_code = 0;
6411
6412 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6413 err_code |= ERR_WARN;
6414
6415 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6416 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006417
6418 curpx->options2 &= ~PR_O2_CHK_ANY;
6419 curpx->options2 |= PR_O2_TCPCHK_CHK;
6420
6421 free_tcpcheck_vars(&rules->preset_vars);
6422 rules->list = NULL;
6423 rules->flags = 0;
6424
6425 cur_arg += 2;
6426 if (*args[cur_arg] && *args[cur_arg+1] &&
6427 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6428 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6429 if (cmd)
6430 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6431 }
6432 else {
6433 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6434 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6435 cmd = strdup("HELO localhost");
6436 }
6437
Christopher Fauletb61caf42020-04-21 10:57:42 +02006438 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006439 if (cmd == NULL || var == NULL) {
6440 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6441 goto error;
6442 }
6443 var->data.type = SMP_T_STR;
6444 var->data.u.str.area = cmd;
6445 var->data.u.str.data = strlen(cmd);
6446 LIST_INIT(&var->list);
6447 LIST_ADDQ(&rules->preset_vars, &var->list);
6448 cmd = NULL;
6449 var = NULL;
6450
Christopher Faulet61cc8522020-04-20 14:54:42 +02006451 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006452 if (rs)
6453 goto ruleset_found;
6454
Christopher Faulet61cc8522020-04-20 14:54:42 +02006455 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006456 if (rs == NULL) {
6457 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6458 goto error;
6459 }
6460
6461 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6462 1, curpx, &rs->rules, file, line, &errmsg);
6463 if (!chk) {
6464 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6465 goto error;
6466 }
6467 chk->index = 0;
6468 LIST_ADDQ(&rs->rules, &chk->list);
6469
6470 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6471 "min-recv", "4",
6472 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006473 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006474 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006475 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006476 if (!chk) {
6477 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6478 goto error;
6479 }
6480 chk->index = 1;
6481 LIST_ADDQ(&rs->rules, &chk->list);
6482
6483 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6484 "min-recv", "4",
6485 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006486 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6487 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006488 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006489 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006490 if (!chk) {
6491 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6492 goto error;
6493 }
6494 chk->index = 2;
6495 LIST_ADDQ(&rs->rules, &chk->list);
6496
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006497 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006498 1, curpx, &rs->rules, file, line, &errmsg);
6499 if (!chk) {
6500 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6501 goto error;
6502 }
6503 chk->index = 3;
6504 LIST_ADDQ(&rs->rules, &chk->list);
6505
6506 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6507 "min-recv", "4",
6508 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006509 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6510 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6511 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006512 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006513 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006514 if (!chk) {
6515 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6516 goto error;
6517 }
6518 chk->index = 4;
6519 LIST_ADDQ(&rs->rules, &chk->list);
6520
Christopher Fauletd7cee712020-04-21 13:45:00 +02006521 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006522
6523 ruleset_found:
6524 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006525 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006526
6527 out:
6528 free(errmsg);
6529 return err_code;
6530
6531 error:
6532 free(cmd);
6533 free(var);
6534 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006535 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006536 err_code |= ERR_ALERT | ERR_FATAL;
6537 goto out;
6538}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006539
Christopher Fauletce355072020-04-02 11:44:39 +02006540/* Parses the "option pgsql-check" proxy keyword */
6541int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6542 const char *file, int line)
6543{
6544 static char pgsql_req[] = {
6545 "%[var(check.plen),htonl,hex]" /* The packet length*/
6546 "00030000" /* the version 3.0 */
6547 "7573657200" /* "user" key */
6548 "%[var(check.username),hex]00" /* the username */
6549 "00"
6550 };
6551
6552 struct tcpcheck_ruleset *rs = NULL;
6553 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6554 struct tcpcheck_rule *chk;
6555 struct tcpcheck_var *var = NULL;
6556 char *user = NULL, *errmsg = NULL;
6557 size_t packetlen = 0;
6558 int err_code = 0;
6559
6560 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6561 err_code |= ERR_WARN;
6562
6563 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6564 goto out;
6565
Christopher Fauletce355072020-04-02 11:44:39 +02006566 curpx->options2 &= ~PR_O2_CHK_ANY;
6567 curpx->options2 |= PR_O2_TCPCHK_CHK;
6568
6569 free_tcpcheck_vars(&rules->preset_vars);
6570 rules->list = NULL;
6571 rules->flags = 0;
6572
6573 cur_arg += 2;
6574 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6575 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6576 file, line, args[0], args[1]);
6577 goto error;
6578 }
6579 if (strcmp(args[cur_arg], "user") == 0) {
6580 packetlen = 15 + strlen(args[cur_arg+1]);
6581 user = strdup(args[cur_arg+1]);
6582
Christopher Fauletb61caf42020-04-21 10:57:42 +02006583 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006584 if (user == NULL || var == NULL) {
6585 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6586 goto error;
6587 }
6588 var->data.type = SMP_T_STR;
6589 var->data.u.str.area = user;
6590 var->data.u.str.data = strlen(user);
6591 LIST_INIT(&var->list);
6592 LIST_ADDQ(&rules->preset_vars, &var->list);
6593 user = NULL;
6594 var = NULL;
6595
Christopher Fauletb61caf42020-04-21 10:57:42 +02006596 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006597 if (var == NULL) {
6598 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6599 goto error;
6600 }
6601 var->data.type = SMP_T_SINT;
6602 var->data.u.sint = packetlen;
6603 LIST_INIT(&var->list);
6604 LIST_ADDQ(&rules->preset_vars, &var->list);
6605 var = NULL;
6606 }
6607 else {
6608 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6609 file, line, args[0], args[1]);
6610 goto error;
6611 }
6612
Christopher Faulet61cc8522020-04-20 14:54:42 +02006613 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006614 if (rs)
6615 goto ruleset_found;
6616
Christopher Faulet61cc8522020-04-20 14:54:42 +02006617 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006618 if (rs == NULL) {
6619 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6620 goto error;
6621 }
6622
6623 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6624 1, curpx, &rs->rules, file, line, &errmsg);
6625 if (!chk) {
6626 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6627 goto error;
6628 }
6629 chk->index = 0;
6630 LIST_ADDQ(&rs->rules, &chk->list);
6631
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006632 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006633 1, curpx, &rs->rules, file, line, &errmsg);
6634 if (!chk) {
6635 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6636 goto error;
6637 }
6638 chk->index = 1;
6639 LIST_ADDQ(&rs->rules, &chk->list);
6640
6641 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6642 "min-recv", "5",
6643 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006644 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006645 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006646 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006647 if (!chk) {
6648 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6649 goto error;
6650 }
6651 chk->index = 2;
6652 LIST_ADDQ(&rs->rules, &chk->list);
6653
Christopher Fauletb841c742020-04-27 18:29:49 +02006654 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^52000000(08|0A|0C)000000(00|02|03|04|05|06)",
Christopher Fauletce355072020-04-02 11:44:39 +02006655 "min-recv", "9",
6656 "error-status", "L7STS",
6657 "on-success", "PostgreSQL server is ok",
6658 "on-error", "PostgreSQL unknown error",
6659 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006660 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006661 if (!chk) {
6662 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6663 goto error;
6664 }
6665 chk->index = 3;
6666 LIST_ADDQ(&rs->rules, &chk->list);
6667
Christopher Fauletd7cee712020-04-21 13:45:00 +02006668 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletce355072020-04-02 11:44:39 +02006669
6670 ruleset_found:
6671 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006672 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006673
6674 out:
6675 free(errmsg);
6676 return err_code;
6677
6678 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006679 free(user);
6680 free(var);
6681 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006682 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006683 err_code |= ERR_ALERT | ERR_FATAL;
6684 goto out;
6685}
6686
6687
6688/* Parses the "option mysql-check" proxy keyword */
6689int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6690 const char *file, int line)
6691{
6692 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6693 * const char mysql40_client_auth_pkt[] = {
6694 * "\x0e\x00\x00" // packet length
6695 * "\x01" // packet number
6696 * "\x00\x00" // client capabilities
6697 * "\x00\x00\x01" // max packet
6698 * "haproxy\x00" // username (null terminated string)
6699 * "\x00" // filler (always 0x00)
6700 * "\x01\x00\x00" // packet length
6701 * "\x00" // packet number
6702 * "\x01" // COM_QUIT command
6703 * };
6704 */
6705 static char mysql40_rsname[] = "*mysql40-check";
6706 static char mysql40_req[] = {
6707 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6708 "0080" /* client capabilities */
6709 "000001" /* max packet */
6710 "%[var(check.username),hex]00" /* the username */
6711 "00" /* filler (always 0x00) */
6712 "010000" /* packet length*/
6713 "00" /* sequence ID */
6714 "01" /* COM_QUIT command */
6715 };
6716
6717 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6718 * const char mysql41_client_auth_pkt[] = {
6719 * "\x0e\x00\x00\" // packet length
6720 * "\x01" // packet number
6721 * "\x00\x00\x00\x00" // client capabilities
6722 * "\x00\x00\x00\x01" // max packet
6723 * "\x21" // character set (UTF-8)
6724 * char[23] // All zeroes
6725 * "haproxy\x00" // username (null terminated string)
6726 * "\x00" // filler (always 0x00)
6727 * "\x01\x00\x00" // packet length
6728 * "\x00" // packet number
6729 * "\x01" // COM_QUIT command
6730 * };
6731 */
6732 static char mysql41_rsname[] = "*mysql41-check";
6733 static char mysql41_req[] = {
6734 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6735 "00820000" /* client capabilities */
6736 "00800001" /* max packet */
6737 "21" /* character set (UTF-8) */
6738 "000000000000000000000000" /* 23 bytes, al zeroes */
6739 "0000000000000000000000"
6740 "%[var(check.username),hex]00" /* the username */
6741 "00" /* filler (always 0x00) */
6742 "010000" /* packet length*/
6743 "00" /* sequence ID */
6744 "01" /* COM_QUIT command */
6745 };
6746
6747 struct tcpcheck_ruleset *rs = NULL;
6748 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6749 struct tcpcheck_rule *chk;
6750 struct tcpcheck_var *var = NULL;
6751 char *mysql_rsname = "*mysql-check";
6752 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6753 int index = 0, err_code = 0;
6754
6755 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6756 err_code |= ERR_WARN;
6757
6758 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6759 goto out;
6760
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006761 curpx->options2 &= ~PR_O2_CHK_ANY;
6762 curpx->options2 |= PR_O2_TCPCHK_CHK;
6763
6764 free_tcpcheck_vars(&rules->preset_vars);
6765 rules->list = NULL;
6766 rules->flags = 0;
6767
6768 cur_arg += 2;
6769 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006770 int packetlen, userlen;
6771
6772 if (strcmp(args[cur_arg], "user") != 0) {
6773 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6774 file, line, args[0], args[1], args[cur_arg]);
6775 goto error;
6776 }
6777
6778 if (*(args[cur_arg+1]) == 0) {
6779 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6780 file, line, args[0], args[1], args[cur_arg]);
6781 goto error;
6782 }
6783
6784 hdr = calloc(4, sizeof(*hdr));
6785 user = strdup(args[cur_arg+1]);
6786 userlen = strlen(args[cur_arg+1]);
6787
6788 if (hdr == NULL || user == NULL) {
6789 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6790 goto error;
6791 }
6792
6793 if (*args[cur_arg+2]) {
6794 if (strcmp(args[cur_arg+2], "post-41") != 0) {
6795 ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41' (got '%s').\n",
6796 file, line, args[cur_arg], args[cur_arg+2]);
6797 goto error;
6798 }
6799 packetlen = userlen + 7 + 27;
6800 mysql_req = mysql41_req;
6801 mysql_rsname = mysql41_rsname;
6802 }
6803 else {
6804 packetlen = userlen + 7;
6805 mysql_req = mysql40_req;
6806 mysql_rsname = mysql40_rsname;
6807 }
6808
6809 hdr[0] = (unsigned char)(packetlen & 0xff);
6810 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6811 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6812 hdr[3] = 1;
6813
Christopher Fauletb61caf42020-04-21 10:57:42 +02006814 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006815 if (var == NULL) {
6816 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6817 goto error;
6818 }
6819 var->data.type = SMP_T_STR;
6820 var->data.u.str.area = hdr;
6821 var->data.u.str.data = 4;
6822 LIST_INIT(&var->list);
6823 LIST_ADDQ(&rules->preset_vars, &var->list);
6824 hdr = NULL;
6825 var = NULL;
6826
Christopher Fauletb61caf42020-04-21 10:57:42 +02006827 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006828 if (var == NULL) {
6829 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6830 goto error;
6831 }
6832 var->data.type = SMP_T_STR;
6833 var->data.u.str.area = user;
6834 var->data.u.str.data = strlen(user);
6835 LIST_INIT(&var->list);
6836 LIST_ADDQ(&rules->preset_vars, &var->list);
6837 user = NULL;
6838 var = NULL;
6839 }
6840
Christopher Faulet61cc8522020-04-20 14:54:42 +02006841 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006842 if (rs)
6843 goto ruleset_found;
6844
Christopher Faulet61cc8522020-04-20 14:54:42 +02006845 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006846 if (rs == NULL) {
6847 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6848 goto error;
6849 }
6850
6851 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6852 1, curpx, &rs->rules, file, line, &errmsg);
6853 if (!chk) {
6854 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6855 goto error;
6856 }
6857 chk->index = index++;
6858 LIST_ADDQ(&rs->rules, &chk->list);
6859
6860 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006861 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006862 1, curpx, &rs->rules, file, line, &errmsg);
6863 if (!chk) {
6864 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6865 goto error;
6866 }
6867 chk->index = index++;
6868 LIST_ADDQ(&rs->rules, &chk->list);
6869 }
6870
6871 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006872 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006873 if (!chk) {
6874 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6875 goto error;
6876 }
6877 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6878 chk->index = index++;
6879 LIST_ADDQ(&rs->rules, &chk->list);
6880
6881 if (mysql_req) {
6882 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006883 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006884 if (!chk) {
6885 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6886 goto error;
6887 }
6888 chk->expect.custom = tcpcheck_mysql_expect_ok;
6889 chk->index = index++;
6890 LIST_ADDQ(&rs->rules, &chk->list);
6891 }
6892
Christopher Fauletd7cee712020-04-21 13:45:00 +02006893 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006894
6895 ruleset_found:
6896 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006897 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006898
6899 out:
6900 free(errmsg);
6901 return err_code;
6902
6903 error:
6904 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006905 free(user);
6906 free(var);
6907 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006908 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006909 err_code |= ERR_ALERT | ERR_FATAL;
6910 goto out;
6911}
6912
Christopher Faulet1997eca2020-04-03 23:13:50 +02006913int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6914 const char *file, int line)
6915{
6916 static char *ldap_req = "300C020101600702010304008000";
6917
6918 struct tcpcheck_ruleset *rs = NULL;
6919 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6920 struct tcpcheck_rule *chk;
6921 char *errmsg = NULL;
6922 int err_code = 0;
6923
6924 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6925 err_code |= ERR_WARN;
6926
6927 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6928 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006929
6930 curpx->options2 &= ~PR_O2_CHK_ANY;
6931 curpx->options2 |= PR_O2_TCPCHK_CHK;
6932
6933 free_tcpcheck_vars(&rules->preset_vars);
6934 rules->list = NULL;
6935 rules->flags = 0;
6936
Christopher Faulet61cc8522020-04-20 14:54:42 +02006937 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006938 if (rs)
6939 goto ruleset_found;
6940
Christopher Faulet61cc8522020-04-20 14:54:42 +02006941 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006942 if (rs == NULL) {
6943 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6944 goto error;
6945 }
6946
6947 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6948 1, curpx, &rs->rules, file, line, &errmsg);
6949 if (!chk) {
6950 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6951 goto error;
6952 }
6953 chk->index = 0;
6954 LIST_ADDQ(&rs->rules, &chk->list);
6955
6956 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6957 "min-recv", "14",
6958 "on-error", "Not LDAPv3 protocol",
6959 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006960 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006961 if (!chk) {
6962 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6963 goto error;
6964 }
6965 chk->index = 1;
6966 LIST_ADDQ(&rs->rules, &chk->list);
6967
6968 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006969 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006970 if (!chk) {
6971 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6972 goto error;
6973 }
6974 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6975 chk->index = 2;
6976 LIST_ADDQ(&rs->rules, &chk->list);
6977
Christopher Fauletd7cee712020-04-21 13:45:00 +02006978 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006979
6980 ruleset_found:
6981 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006982 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006983
6984 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006985 free(errmsg);
6986 return err_code;
6987
6988 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006989 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006990 err_code |= ERR_ALERT | ERR_FATAL;
6991 goto out;
6992}
6993
6994int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6995 const char *file, int line)
6996{
6997 struct tcpcheck_ruleset *rs = NULL;
6998 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6999 struct tcpcheck_rule *chk;
7000 char *spop_req = NULL;
7001 char *errmsg = NULL;
7002 int spop_len = 0, err_code = 0;
7003
7004 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7005 err_code |= ERR_WARN;
7006
7007 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7008 goto out;
7009
Christopher Faulet267b01b2020-04-04 10:27:09 +02007010 curpx->options2 &= ~PR_O2_CHK_ANY;
7011 curpx->options2 |= PR_O2_TCPCHK_CHK;
7012
7013 free_tcpcheck_vars(&rules->preset_vars);
7014 rules->list = NULL;
7015 rules->flags = 0;
7016
7017
Christopher Faulet61cc8522020-04-20 14:54:42 +02007018 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007019 if (rs)
7020 goto ruleset_found;
7021
Christopher Faulet61cc8522020-04-20 14:54:42 +02007022 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007023 if (rs == NULL) {
7024 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7025 goto error;
7026 }
7027
7028 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7029 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7030 goto error;
7031 }
7032 chunk_reset(&trash);
7033 dump_binary(&trash, spop_req, spop_len);
7034 trash.area[trash.data] = '\0';
7035
7036 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7037 1, curpx, &rs->rules, file, line, &errmsg);
7038 if (!chk) {
7039 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7040 goto error;
7041 }
7042 chk->index = 0;
7043 LIST_ADDQ(&rs->rules, &chk->list);
7044
7045 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007046 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007047 if (!chk) {
7048 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7049 goto error;
7050 }
7051 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7052 chk->index = 1;
7053 LIST_ADDQ(&rs->rules, &chk->list);
7054
Christopher Fauletd7cee712020-04-21 13:45:00 +02007055 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007056
7057 ruleset_found:
7058 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007059 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007060
7061 out:
7062 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007063 free(errmsg);
7064 return err_code;
7065
7066 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007067 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007068 err_code |= ERR_ALERT | ERR_FATAL;
7069 goto out;
7070}
Christopher Fauletce355072020-04-02 11:44:39 +02007071
Christopher Faulete5870d82020-04-15 11:32:03 +02007072
7073struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7074{
7075 struct tcpcheck_rule *chk = NULL;
7076 struct tcpcheck_http_hdr *hdr = NULL;
7077 char *meth = NULL, *uri = NULL, *vsn = NULL;
7078 char *hdrs, *body;
7079
7080 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7081 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7082 if (hdrs == body)
7083 hdrs = NULL;
7084 if (hdrs) {
7085 *hdrs = '\0';
7086 hdrs +=2;
7087 }
7088 if (body) {
7089 *body = '\0';
7090 body += 4;
7091 }
7092 if (hdrs || body) {
7093 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7094 " Please, consider to use 'http-check send' directive instead.");
7095 }
7096
7097 chk = calloc(1, sizeof(*chk));
7098 if (!chk) {
7099 memprintf(errmsg, "out of memory");
7100 goto error;
7101 }
7102 chk->action = TCPCHK_ACT_SEND;
7103 chk->send.type = TCPCHK_SEND_HTTP;
7104 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7105 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7106 LIST_INIT(&chk->send.http.hdrs);
7107
7108 /* Copy the method, uri and version */
7109 if (*args[cur_arg]) {
7110 if (!*args[cur_arg+1])
7111 uri = args[cur_arg];
7112 else
7113 meth = args[cur_arg];
7114 }
7115 if (*args[cur_arg+1])
7116 uri = args[cur_arg+1];
7117 if (*args[cur_arg+2])
7118 vsn = args[cur_arg+2];
7119
7120 if (meth) {
7121 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7122 chk->send.http.meth.str.area = strdup(meth);
7123 chk->send.http.meth.str.data = strlen(meth);
7124 if (!chk->send.http.meth.str.area) {
7125 memprintf(errmsg, "out of memory");
7126 goto error;
7127 }
7128 }
7129 if (uri) {
7130 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007131 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007132 memprintf(errmsg, "out of memory");
7133 goto error;
7134 }
7135 }
7136 if (vsn) {
7137 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007138 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007139 memprintf(errmsg, "out of memory");
7140 goto error;
7141 }
7142 }
7143
7144 /* Copy the header */
7145 if (hdrs) {
7146 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7147 struct h1m h1m;
7148 int i, ret;
7149
7150 /* Build and parse the request */
7151 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7152
7153 h1m.flags = H1_MF_HDRS_ONLY;
7154 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7155 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7156 &h1m, NULL);
7157 if (ret <= 0) {
7158 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7159 goto error;
7160 }
7161
Christopher Fauletb61caf42020-04-21 10:57:42 +02007162 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007163 hdr = calloc(1, sizeof(*hdr));
7164 if (!hdr) {
7165 memprintf(errmsg, "out of memory");
7166 goto error;
7167 }
7168 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007169 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007170 if (!hdr->name.ptr) {
7171 memprintf(errmsg, "out of memory");
7172 goto error;
7173 }
7174
Christopher Fauletb61caf42020-04-21 10:57:42 +02007175 ist0(tmp_hdrs[i].v);
7176 if (!parse_logformat_string(istptr(tmp_hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
Christopher Faulete5870d82020-04-15 11:32:03 +02007177 goto error;
7178 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7179 }
7180 }
7181
7182 /* Copy the body */
7183 if (body) {
7184 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007185 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007186 memprintf(errmsg, "out of memory");
7187 goto error;
7188 }
7189 }
7190
7191 return chk;
7192
7193 error:
7194 free_tcpcheck_http_hdr(hdr);
7195 free_tcpcheck(chk, 0);
7196 return NULL;
7197}
7198
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007199int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7200 const char *file, int line)
7201{
Christopher Faulete5870d82020-04-15 11:32:03 +02007202 struct tcpcheck_ruleset *rs = NULL;
7203 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7204 struct tcpcheck_rule *chk;
7205 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007206 int err_code = 0;
7207
7208 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7209 err_code |= ERR_WARN;
7210
7211 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7212 goto out;
7213
Christopher Faulete5870d82020-04-15 11:32:03 +02007214 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7215 if (!chk) {
7216 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7217 goto error;
7218 }
7219 if (errmsg) {
7220 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7221 err_code |= ERR_WARN;
7222 free(errmsg);
7223 errmsg = NULL;
7224 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007225
Christopher Faulete5870d82020-04-15 11:32:03 +02007226 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007227 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007228 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007229
Christopher Faulete5870d82020-04-15 11:32:03 +02007230 free_tcpcheck_vars(&rules->preset_vars);
7231 rules->list = NULL;
7232 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007233
Christopher Faulete5870d82020-04-15 11:32:03 +02007234 /* Deduce the ruleset name from the proxy info */
7235 chunk_printf(&trash, "*http-check-%s_%s-%d",
7236 ((curpx == defpx) ? "defaults" : curpx->id),
7237 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007238
Christopher Faulet61cc8522020-04-20 14:54:42 +02007239 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007240 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007241 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007242 if (rs == NULL) {
7243 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7244 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007245 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007246 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007247
Christopher Faulete5870d82020-04-15 11:32:03 +02007248 rules->list = &rs->rules;
7249 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7250 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7251 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7252 rules->list = NULL;
7253 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007254 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007255
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007256 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007257 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007258 return err_code;
7259
7260 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007261 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007262 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007263 err_code |= ERR_ALERT | ERR_FATAL;
7264 goto out;
7265}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007266
Christopher Faulet6f557912020-04-09 15:58:50 +02007267int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7268 const char *file, int line)
7269{
7270 int err_code = 0;
7271
Christopher Faulet6f557912020-04-09 15:58:50 +02007272 curpx->options2 &= ~PR_O2_CHK_ANY;
7273 curpx->options2 |= PR_O2_EXT_CHK;
7274 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7275 goto out;
7276
7277 out:
7278 return err_code;
7279}
7280
Christopher Fauletce8111e2020-04-06 15:04:11 +02007281/* Parse the "addr" server keyword */
7282static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7283 char **errmsg)
7284{
7285 struct sockaddr_storage *sk;
7286 struct protocol *proto;
7287 int port1, port2, err_code = 0;
7288
7289
7290 if (!*args[*cur_arg+1]) {
7291 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7292 goto error;
7293 }
7294
7295 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7296 if (!sk) {
7297 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7298 goto error;
7299 }
7300
7301 proto = protocol_by_family(sk->ss_family);
7302 if (!proto || !proto->connect) {
7303 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7304 args[*cur_arg], args[*cur_arg+1]);
7305 goto error;
7306 }
7307
7308 if (port1 != port2) {
7309 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7310 args[*cur_arg], args[*cur_arg+1]);
7311 goto error;
7312 }
7313
7314 srv->check.addr = srv->agent.addr = *sk;
7315 srv->flags |= SRV_F_CHECKADDR;
7316 srv->flags |= SRV_F_AGENTADDR;
7317
7318 out:
7319 return err_code;
7320
7321 error:
7322 err_code |= ERR_ALERT | ERR_FATAL;
7323 goto out;
7324}
7325
7326
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007327/* Parse the "agent-addr" server keyword */
7328static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7329 char **errmsg)
7330{
7331 int err_code = 0;
7332
7333 if (!*(args[*cur_arg+1])) {
7334 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7335 goto error;
7336 }
7337 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7338 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7339 goto error;
7340 }
7341
7342 out:
7343 return err_code;
7344
7345 error:
7346 err_code |= ERR_ALERT | ERR_FATAL;
7347 goto out;
7348}
7349
7350/* Parse the "agent-check" server keyword */
7351static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7352 char **errmsg)
7353{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007354 struct tcpcheck_ruleset *rs = NULL;
7355 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7356 struct tcpcheck_rule *chk;
7357 int err_code = 0;
7358
7359 if (srv->do_agent)
7360 goto out;
7361
7362 if (!rules) {
7363 rules = calloc(1, sizeof(*rules));
7364 if (!rules) {
7365 memprintf(errmsg, "out of memory.");
7366 goto error;
7367 }
7368 LIST_INIT(&rules->preset_vars);
7369 srv->agent.tcpcheck_rules = rules;
7370 }
7371 rules->list = NULL;
7372 rules->flags = 0;
7373
Christopher Faulet61cc8522020-04-20 14:54:42 +02007374 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007375 if (rs)
7376 goto ruleset_found;
7377
Christopher Faulet61cc8522020-04-20 14:54:42 +02007378 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007379 if (rs == NULL) {
7380 memprintf(errmsg, "out of memory.");
7381 goto error;
7382 }
7383
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007384 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007385 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7386 if (!chk) {
7387 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7388 goto error;
7389 }
7390 chk->index = 0;
7391 LIST_ADDQ(&rs->rules, &chk->list);
7392
7393 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007394 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7395 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007396 if (!chk) {
7397 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7398 goto error;
7399 }
7400 chk->expect.custom = tcpcheck_agent_expect_reply;
7401 chk->index = 1;
7402 LIST_ADDQ(&rs->rules, &chk->list);
7403
Christopher Fauletd7cee712020-04-21 13:45:00 +02007404 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007405
7406 ruleset_found:
7407 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007408 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007409 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007410
7411 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007412 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007413
7414 error:
7415 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007416 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007417 err_code |= ERR_ALERT | ERR_FATAL;
7418 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007419}
7420
7421/* Parse the "agent-inter" server keyword */
7422static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7423 char **errmsg)
7424{
7425 const char *err = NULL;
7426 unsigned int delay;
7427 int err_code = 0;
7428
7429 if (!*(args[*cur_arg+1])) {
7430 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7431 goto error;
7432 }
7433
7434 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7435 if (err == PARSE_TIME_OVER) {
7436 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7437 args[*cur_arg+1], args[*cur_arg], srv->id);
7438 goto error;
7439 }
7440 else if (err == PARSE_TIME_UNDER) {
7441 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7442 args[*cur_arg+1], args[*cur_arg], srv->id);
7443 goto error;
7444 }
7445 else if (err) {
7446 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7447 *err, srv->id);
7448 goto error;
7449 }
7450 if (delay <= 0) {
7451 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7452 delay, args[*cur_arg], srv->id);
7453 goto error;
7454 }
7455 srv->agent.inter = delay;
7456
7457 out:
7458 return err_code;
7459
7460 error:
7461 err_code |= ERR_ALERT | ERR_FATAL;
7462 goto out;
7463}
7464
7465/* Parse the "agent-port" server keyword */
7466static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7467 char **errmsg)
7468{
7469 int err_code = 0;
7470
7471 if (!*(args[*cur_arg+1])) {
7472 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7473 goto error;
7474 }
7475
7476 global.maxsock++;
7477 srv->agent.port = atol(args[*cur_arg+1]);
7478
7479 out:
7480 return err_code;
7481
7482 error:
7483 err_code |= ERR_ALERT | ERR_FATAL;
7484 goto out;
7485}
7486
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007487int set_srv_agent_send(struct server *srv, const char *send)
7488{
7489 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7490 struct tcpcheck_var *var = NULL;
7491 char *str;
7492
7493 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007494 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007495 if (str == NULL || var == NULL)
7496 goto error;
7497
7498 free_tcpcheck_vars(&rules->preset_vars);
7499
7500 var->data.type = SMP_T_STR;
7501 var->data.u.str.area = str;
7502 var->data.u.str.data = strlen(str);
7503 LIST_INIT(&var->list);
7504 LIST_ADDQ(&rules->preset_vars, &var->list);
7505
7506 return 1;
7507
7508 error:
7509 free(str);
7510 free(var);
7511 return 0;
7512}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007513
7514/* Parse the "agent-send" server keyword */
7515static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7516 char **errmsg)
7517{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007518 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007519 int err_code = 0;
7520
7521 if (!*(args[*cur_arg+1])) {
7522 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7523 goto error;
7524 }
7525
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007526 if (!rules) {
7527 rules = calloc(1, sizeof(*rules));
7528 if (!rules) {
7529 memprintf(errmsg, "out of memory.");
7530 goto error;
7531 }
7532 LIST_INIT(&rules->preset_vars);
7533 srv->agent.tcpcheck_rules = rules;
7534 }
7535
7536 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007537 memprintf(errmsg, "out of memory.");
7538 goto error;
7539 }
7540
7541 out:
7542 return err_code;
7543
7544 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007545 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007546 err_code |= ERR_ALERT | ERR_FATAL;
7547 goto out;
7548}
7549
7550/* Parse the "no-agent-send" server keyword */
7551static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7552 char **errmsg)
7553{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007554 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007555 return 0;
7556}
7557
Christopher Fauletce8111e2020-04-06 15:04:11 +02007558/* Parse the "check" server keyword */
7559static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7560 char **errmsg)
7561{
7562 srv->do_check = 1;
7563 return 0;
7564}
7565
7566/* Parse the "check-send-proxy" server keyword */
7567static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7568 char **errmsg)
7569{
7570 srv->check.send_proxy = 1;
7571 return 0;
7572}
7573
7574/* Parse the "check-via-socks4" server keyword */
7575static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7576 char **errmsg)
7577{
7578 srv->check.via_socks4 = 1;
7579 return 0;
7580}
7581
7582/* Parse the "no-check" server keyword */
7583static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7584 char **errmsg)
7585{
7586 deinit_srv_check(srv);
7587 return 0;
7588}
7589
7590/* Parse the "no-check-send-proxy" server keyword */
7591static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7592 char **errmsg)
7593{
7594 srv->check.send_proxy = 0;
7595 return 0;
7596}
7597
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007598/* parse the "check-proto" server keyword */
7599static int srv_parse_check_proto(char **args, int *cur_arg,
7600 struct proxy *px, struct server *newsrv, char **err)
7601{
7602 int err_code = 0;
7603
7604 if (!*args[*cur_arg + 1]) {
7605 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7606 goto error;
7607 }
7608 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7609 if (!newsrv->check.mux_proto) {
7610 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7611 goto error;
7612 }
7613
7614 out:
7615 return err_code;
7616
7617 error:
7618 err_code |= ERR_ALERT | ERR_FATAL;
7619 goto out;
7620}
7621
7622
Christopher Fauletce8111e2020-04-06 15:04:11 +02007623/* Parse the "rise" server keyword */
7624static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7625 char **errmsg)
7626{
7627 int err_code = 0;
7628
7629 if (!*args[*cur_arg + 1]) {
7630 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7631 goto error;
7632 }
7633
7634 srv->check.rise = atol(args[*cur_arg+1]);
7635 if (srv->check.rise <= 0) {
7636 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7637 goto error;
7638 }
7639
7640 if (srv->check.health)
7641 srv->check.health = srv->check.rise;
7642
7643 out:
7644 return err_code;
7645
7646 error:
7647 deinit_srv_agent_check(srv);
7648 err_code |= ERR_ALERT | ERR_FATAL;
7649 goto out;
7650 return 0;
7651}
7652
7653/* Parse the "fall" server keyword */
7654static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7655 char **errmsg)
7656{
7657 int err_code = 0;
7658
7659 if (!*args[*cur_arg + 1]) {
7660 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7661 goto error;
7662 }
7663
7664 srv->check.fall = atol(args[*cur_arg+1]);
7665 if (srv->check.fall <= 0) {
7666 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7667 goto error;
7668 }
7669
7670 out:
7671 return err_code;
7672
7673 error:
7674 deinit_srv_agent_check(srv);
7675 err_code |= ERR_ALERT | ERR_FATAL;
7676 goto out;
7677 return 0;
7678}
7679
7680/* Parse the "inter" server keyword */
7681static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7682 char **errmsg)
7683{
7684 const char *err = NULL;
7685 unsigned int delay;
7686 int err_code = 0;
7687
7688 if (!*(args[*cur_arg+1])) {
7689 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7690 goto error;
7691 }
7692
7693 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7694 if (err == PARSE_TIME_OVER) {
7695 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7696 args[*cur_arg+1], args[*cur_arg], srv->id);
7697 goto error;
7698 }
7699 else if (err == PARSE_TIME_UNDER) {
7700 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7701 args[*cur_arg+1], args[*cur_arg], srv->id);
7702 goto error;
7703 }
7704 else if (err) {
7705 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7706 *err, srv->id);
7707 goto error;
7708 }
7709 if (delay <= 0) {
7710 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7711 delay, args[*cur_arg], srv->id);
7712 goto error;
7713 }
7714 srv->check.inter = delay;
7715
7716 out:
7717 return err_code;
7718
7719 error:
7720 err_code |= ERR_ALERT | ERR_FATAL;
7721 goto out;
7722}
7723
7724
7725/* Parse the "fastinter" server keyword */
7726static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7727 char **errmsg)
7728{
7729 const char *err = NULL;
7730 unsigned int delay;
7731 int err_code = 0;
7732
7733 if (!*(args[*cur_arg+1])) {
7734 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7735 goto error;
7736 }
7737
7738 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7739 if (err == PARSE_TIME_OVER) {
7740 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7741 args[*cur_arg+1], args[*cur_arg], srv->id);
7742 goto error;
7743 }
7744 else if (err == PARSE_TIME_UNDER) {
7745 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7746 args[*cur_arg+1], args[*cur_arg], srv->id);
7747 goto error;
7748 }
7749 else if (err) {
7750 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7751 *err, srv->id);
7752 goto error;
7753 }
7754 if (delay <= 0) {
7755 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7756 delay, args[*cur_arg], srv->id);
7757 goto error;
7758 }
7759 srv->check.fastinter = delay;
7760
7761 out:
7762 return err_code;
7763
7764 error:
7765 err_code |= ERR_ALERT | ERR_FATAL;
7766 goto out;
7767}
7768
7769
7770/* Parse the "downinter" server keyword */
7771static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7772 char **errmsg)
7773{
7774 const char *err = NULL;
7775 unsigned int delay;
7776 int err_code = 0;
7777
7778 if (!*(args[*cur_arg+1])) {
7779 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7780 goto error;
7781 }
7782
7783 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7784 if (err == PARSE_TIME_OVER) {
7785 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7786 args[*cur_arg+1], args[*cur_arg], srv->id);
7787 goto error;
7788 }
7789 else if (err == PARSE_TIME_UNDER) {
7790 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7791 args[*cur_arg+1], args[*cur_arg], srv->id);
7792 goto error;
7793 }
7794 else if (err) {
7795 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7796 *err, srv->id);
7797 goto error;
7798 }
7799 if (delay <= 0) {
7800 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7801 delay, args[*cur_arg], srv->id);
7802 goto error;
7803 }
7804 srv->check.downinter = delay;
7805
7806 out:
7807 return err_code;
7808
7809 error:
7810 err_code |= ERR_ALERT | ERR_FATAL;
7811 goto out;
7812}
7813
7814/* Parse the "port" server keyword */
7815static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7816 char **errmsg)
7817{
7818 int err_code = 0;
7819
7820 if (!*(args[*cur_arg+1])) {
7821 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7822 goto error;
7823 }
7824
7825 global.maxsock++;
7826 srv->check.port = atol(args[*cur_arg+1]);
7827 srv->flags |= SRV_F_CHECKPORT;
7828
7829 out:
7830 return err_code;
7831
7832 error:
7833 err_code |= ERR_ALERT | ERR_FATAL;
7834 goto out;
7835}
7836
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007837static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007838 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7839 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7840 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007841 { 0, NULL, NULL },
7842}};
7843
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007844static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007845 { "addr", srv_parse_addr, 1, 1 }, /* IP address to send health to or to probe from agent-check */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007846 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7847 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7848 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7849 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7850 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007851 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007852 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007853 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7854 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007855 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007856 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7857 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7858 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7859 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7860 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7861 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7862 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7863 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007864 { NULL, NULL, 0 },
7865}};
7866
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007867INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007868INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007869
Willy Tarreaubd741542010-03-16 18:46:54 +01007870/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007871 * Local variables:
7872 * c-indent-level: 8
7873 * c-basic-offset: 8
7874 * End:
7875 */