blob: 8d371e652d5c48cf42917541dc7c9a8cba146a3b [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;
597 case TCPCHK_EXPECT_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200598 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100599 break;
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100600 case TCPCHK_EXPECT_REGEX_BINARY:
601 chunk_appendf(chk, " (expect binary regex)");
602 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200603 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200604 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200605 break;
606 case TCPCHK_EXPECT_HTTP_REGEX_STATUS:
607 chunk_appendf(chk, " (expect HTTP status regex)");
608 break;
609 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200610 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 break;
612 case TCPCHK_EXPECT_HTTP_REGEX_BODY:
613 chunk_appendf(chk, " (expect HTTP body regex)");
614 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200615 case TCPCHK_EXPECT_CUSTOM:
616 chunk_appendf(chk, " (expect custom function)");
617 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100618 case TCPCHK_EXPECT_UNDEF:
619 chunk_appendf(chk, " (undefined expect!)");
620 break;
621 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200622 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200623 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200624 chunk_appendf(chk, " (send)");
625 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200626
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200627 if (check->current_step && check->current_step->comment)
628 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200629 }
630 }
631
Willy Tarreau00149122017-10-04 18:05:01 +0200632 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100633 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200634 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
635 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100636 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200637 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
638 chk->area);
639 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100640 }
641 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100642 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200643 chunk_printf(&trash, "%s%s", strerror(errno),
644 chk->area);
645 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100646 }
647 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200648 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100649 }
650 }
651
Willy Tarreau00149122017-10-04 18:05:01 +0200652 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200653 /* NOTE: this is reported after <fall> tries */
654 chunk_printf(chk, "No port available for the TCP connection");
655 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
656 }
657
Willy Tarreau00149122017-10-04 18:05:01 +0200658 if (!conn) {
659 /* connection allocation error before the connection was established */
660 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
661 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100662 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100663 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200664 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100665 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
666 else if (expired)
667 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200668
669 /*
670 * might be due to a server IP change.
671 * Let's trigger a DNS resolution if none are currently running.
672 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100673 if (check->server)
674 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200675
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100676 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100677 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100678 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200679 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100680 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
681 else if (expired)
682 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
683 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200684 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100685 /* I/O error after connection was established and before we could diagnose */
686 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
687 }
688 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200689 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
690
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100691 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200692 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
693 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200694 tout = check->current_step->expect.tout_status;
695 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100696 }
697
698 return;
699}
700
Willy Tarreaubaaee002006-06-26 02:48:02 +0200701
Christopher Faulet61cc8522020-04-20 14:54:42 +0200702/**************************************************************************/
703/*************** Init/deinit tcp-check rules and ruleset ******************/
704/**************************************************************************/
705/* Releases memory allocated for a log-format string */
706static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200707{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200708 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100709
Christopher Faulet61cc8522020-04-20 14:54:42 +0200710 list_for_each_entry_safe(lf, lfb, fmt, list) {
711 LIST_DEL(&lf->list);
712 release_sample_expr(lf->expr);
713 free(lf->arg);
714 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100715 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200716}
717
Christopher Faulet61cc8522020-04-20 14:54:42 +0200718/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
719static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100720{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200721 if (!hdr)
722 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200723
Christopher Faulet61cc8522020-04-20 14:54:42 +0200724 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200725 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200726 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100727}
728
Christopher Faulet61cc8522020-04-20 14:54:42 +0200729/* Releases memory allocated for an HTTP header list used in a tcp-check send
730 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200731 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200732static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200733{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200734 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200735
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
737 LIST_DEL(&hdr->list);
738 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200739 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200740}
741
Christopher Faulet61cc8522020-04-20 14:54:42 +0200742/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
743 * tcp-check was allocated using a memory pool (it is used to instantiate email
744 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200745 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200746static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200747{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200748 if (!rule)
749 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200750
Christopher Faulet61cc8522020-04-20 14:54:42 +0200751 free(rule->comment);
752 switch (rule->action) {
753 case TCPCHK_ACT_SEND:
754 switch (rule->send.type) {
755 case TCPCHK_SEND_STRING:
756 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200757 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200758 break;
759 case TCPCHK_SEND_STRING_LF:
760 case TCPCHK_SEND_BINARY_LF:
761 free_tcpcheck_fmt(&rule->send.fmt);
762 break;
763 case TCPCHK_SEND_HTTP:
764 free(rule->send.http.meth.str.area);
765 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200766 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200767 else
768 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200769 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200770 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
771 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200772 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200773 else
774 free_tcpcheck_fmt(&rule->send.http.body_fmt);
775 break;
776 case TCPCHK_SEND_UNDEF:
777 break;
778 }
779 break;
780 case TCPCHK_ACT_EXPECT:
781 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
782 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
783 release_sample_expr(rule->expect.status_expr);
784 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200785 case TCPCHK_EXPECT_HTTP_STATUS:
786 free(rule->expect.codes.codes);
787 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200788 case TCPCHK_EXPECT_STRING:
789 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200790 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200791 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200792 break;
793 case TCPCHK_EXPECT_REGEX:
794 case TCPCHK_EXPECT_REGEX_BINARY:
795 case TCPCHK_EXPECT_HTTP_REGEX_STATUS:
796 case TCPCHK_EXPECT_HTTP_REGEX_BODY:
797 regex_free(rule->expect.regex);
798 break;
799 case TCPCHK_EXPECT_CUSTOM:
800 case TCPCHK_EXPECT_UNDEF:
801 break;
802 }
803 break;
804 case TCPCHK_ACT_CONNECT:
805 free(rule->connect.sni);
806 free(rule->connect.alpn);
807 release_sample_expr(rule->connect.port_expr);
808 break;
809 case TCPCHK_ACT_COMMENT:
810 break;
811 case TCPCHK_ACT_ACTION_KW:
812 free(rule->action_kw.rule);
813 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200814 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200815
816 if (in_pool)
817 pool_free(pool_head_tcpcheck_rule, rule);
818 else
819 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200820}
821
Christopher Faulet61cc8522020-04-20 14:54:42 +0200822/* Creates a tcp-check variable used in preset variables before executing a
823 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100824 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200825static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100826{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200827 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100828
Christopher Faulet61cc8522020-04-20 14:54:42 +0200829 var = calloc(1, sizeof(*var));
830 if (var == NULL)
831 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100832
Christopher Fauletb61caf42020-04-21 10:57:42 +0200833 var->name = istdup(name);
834 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200835 free(var);
836 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100837 }
Simon Horman98637e52014-06-20 12:30:16 +0900838
Christopher Faulet61cc8522020-04-20 14:54:42 +0200839 LIST_INIT(&var->list);
840 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900841}
842
Christopher Faulet61cc8522020-04-20 14:54:42 +0200843/* Releases memory allocated for a preset tcp-check variable */
844static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900845{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200846 if (!var)
847 return;
848
Christopher Fauletb61caf42020-04-21 10:57:42 +0200849 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200850 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
851 free(var->data.u.str.area);
852 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
853 free(var->data.u.meth.str.area);
854 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900855}
856
Christopher Faulet61cc8522020-04-20 14:54:42 +0200857/* Releases a list of preset tcp-check variables */
858static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900859{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200860 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200861
Christopher Faulet61cc8522020-04-20 14:54:42 +0200862 list_for_each_entry_safe(var, back, vars, list) {
863 LIST_DEL(&var->list);
864 free_tcpcheck_var(var);
865 }
Simon Horman98637e52014-06-20 12:30:16 +0900866}
867
Christopher Faulet61cc8522020-04-20 14:54:42 +0200868/* Duplicate a list of preset tcp-check variables */
869int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900870{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200871 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900872
Christopher Faulet61cc8522020-04-20 14:54:42 +0200873 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200874 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200875 if (!new)
876 goto error;
877 new->data.type = var->data.type;
878 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
879 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
880 goto error;
881 if (var->data.type == SMP_T_STR)
882 new->data.u.str.area[new->data.u.str.data] = 0;
883 }
884 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
885 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
886 goto error;
887 new->data.u.str.area[new->data.u.str.data] = 0;
888 new->data.u.meth.meth = var->data.u.meth.meth;
889 }
890 else
891 new->data.u = var->data.u;
892 LIST_ADDQ(dst, &new->list);
893 }
894 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900895
Christopher Faulet61cc8522020-04-20 14:54:42 +0200896 error:
897 free(new);
898 return 0;
899}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200900
Christopher Faulet61cc8522020-04-20 14:54:42 +0200901/* Looks for a shared tcp-check ruleset given its name. */
902static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
903{
904 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200905 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900906
Christopher Fauletd7cee712020-04-21 13:45:00 +0200907 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
908 if (node) {
909 rs = container_of(node, typeof(*rs), node);
910 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200911 }
912 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900913}
914
Christopher Faulet61cc8522020-04-20 14:54:42 +0200915/* Creates a new shared tcp-check ruleset */
916static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900917{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200918 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900919
Christopher Faulet61cc8522020-04-20 14:54:42 +0200920 rs = calloc(1, sizeof(*rs));
921 if (rs == NULL)
922 return NULL;
923
Christopher Fauletd7cee712020-04-21 13:45:00 +0200924 rs->node.key = strdup(name);
925 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200926 free(rs);
927 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900928 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200929
Christopher Faulet61cc8522020-04-20 14:54:42 +0200930 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200931 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200932 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900933}
934
Christopher Faulet61cc8522020-04-20 14:54:42 +0200935/* Releases memory allocated by a tcp-check ruleset. */
936static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900937{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200938 struct tcpcheck_rule *r, *rb;
939 if (!rs)
940 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200941
Christopher Fauletd7cee712020-04-21 13:45:00 +0200942 ebpt_delete(&rs->node);
943 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200944 list_for_each_entry_safe(r, rb, &rs->rules, list) {
945 LIST_DEL(&r->list);
946 free_tcpcheck(r, 0);
947 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200948 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900949}
950
Christopher Faulet61cc8522020-04-20 14:54:42 +0200951
952/**************************************************************************/
953/**************** Everything about tcp-checks execution *******************/
954/**************************************************************************/
955/* Returns the id of a step in a tcp-check ruleset */
956static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200957{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200958 if (!rule)
959 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900960
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961 /* no last started step => first step */
962 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900963 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900964
Christopher Faulet61cc8522020-04-20 14:54:42 +0200965 /* last step is the first implicit connect */
966 if (rule->index == 0 &&
967 rule->action == TCPCHK_ACT_CONNECT &&
968 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
969 return 0;
Simon Horman98637e52014-06-20 12:30:16 +0900970
Christopher Faulet61cc8522020-04-20 14:54:42 +0200971 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +0900972}
973
Christopher Faulet61cc8522020-04-20 14:54:42 +0200974/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
975 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +0100976 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200977static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +0100978{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200979 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +0100980
Christopher Faulet61cc8522020-04-20 14:54:42 +0200981 list_for_each_entry(r, rules->list, list) {
982 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
983 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +0100984 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200985 return NULL;
986}
Cyril Bontéac92a062014-12-27 22:28:38 +0100987
Christopher Faulet61cc8522020-04-20 14:54:42 +0200988/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
989 * NULL if none was found.
990 */
991static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
992{
993 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +0100994
Christopher Faulet61cc8522020-04-20 14:54:42 +0200995 list_for_each_entry_rev(r, rules->list, list) {
996 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
997 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +0100998 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200999 return NULL;
1000}
Cyril Bontéac92a062014-12-27 22:28:38 +01001001
Christopher Faulet61cc8522020-04-20 14:54:42 +02001002/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1003 * <start> or NULL if non was found. If <start> is NULL, it relies on
1004 * get_first_tcpcheck_rule().
1005 */
1006static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1007{
1008 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001009
Christopher Faulet61cc8522020-04-20 14:54:42 +02001010 if (!start)
1011 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001012
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 r = LIST_NEXT(&start->list, typeof(r), list);
1014 list_for_each_entry_from(r, rules->list, list) {
1015 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1016 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001017 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001018 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001019}
Simon Horman98637e52014-06-20 12:30:16 +09001020
Simon Horman98637e52014-06-20 12:30:16 +09001021
Christopher Faulet61cc8522020-04-20 14:54:42 +02001022/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1023static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1024 int match, struct ist info)
1025{
1026 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001027
Christopher Faulet61cc8522020-04-20 14:54:42 +02001028 /* Follows these step to produce the info message:
1029 * 1. if info field is already provided, copy it
1030 * 2. if the expect rule provides an onerror log-format string,
1031 * use it to produce the message
1032 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1033 * 4. Otherwise produce the generic tcp-check info message
1034 */
1035 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001036 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001037 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001038 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001039 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1040 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1041 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001042 }
Simon Horman98637e52014-06-20 12:30:16 +09001043
Christopher Faulet61cc8522020-04-20 14:54:42 +02001044 if (check->type == PR_O2_TCPCHK_CHK &&
1045 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1046 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001047
Christopher Faulet61cc8522020-04-20 14:54:42 +02001048 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1049 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001051 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1052 break;
1053 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001054 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001055 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001056 tcpcheck_get_step_id(check, rule));
1057 break;
1058 case TCPCHK_EXPECT_BINARY:
1059 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1060 break;
1061 case TCPCHK_EXPECT_REGEX:
1062 case TCPCHK_EXPECT_HTTP_REGEX_STATUS:
1063 case TCPCHK_EXPECT_HTTP_REGEX_BODY:
1064 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1065 break;
1066 case TCPCHK_EXPECT_REGEX_BINARY:
1067 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001068 break;
1069 case TCPCHK_EXPECT_CUSTOM:
1070 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1071 break;
1072 case TCPCHK_EXPECT_UNDEF:
1073 /* Should never happen. */
1074 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001075 }
1076
Christopher Faulet61cc8522020-04-20 14:54:42 +02001077 comment:
1078 /* If the failing expect rule provides a comment, it is concatenated to
1079 * the info message.
1080 */
1081 if (rule->comment) {
1082 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001083 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001084 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001085
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 /* Finally, the check status code is set if the failing expect rule
1087 * defines a status expression.
1088 */
1089 if (rule->expect.status_expr) {
1090 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001091 rule->expect.status_expr, SMP_T_STR);
1092
1093 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1094 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001095 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001096 }
Simon Horman98637e52014-06-20 12:30:16 +09001097
Christopher Faulet61cc8522020-04-20 14:54:42 +02001098 *(b_tail(msg)) = '\0';
1099}
Cyril Bontéac92a062014-12-27 22:28:38 +01001100
Christopher Faulet61cc8522020-04-20 14:54:42 +02001101/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1102static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1103 struct ist info)
1104{
1105 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001106
Christopher Faulet61cc8522020-04-20 14:54:42 +02001107 /* Follows these step to produce the info message:
1108 * 1. if info field is already provided, copy it
1109 * 2. if the expect rule provides an onsucces log-format string,
1110 * use it to produce the message
1111 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1112 * 4. Otherwise produce the generic tcp-check info message
1113 */
1114 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001115 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001116 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1117 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1118 &rule->expect.onsuccess_fmt);
1119 else if (check->type == PR_O2_TCPCHK_CHK &&
1120 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1121 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001122
Christopher Faulet61cc8522020-04-20 14:54:42 +02001123 /* Finally, the check status code is set if the expect rule defines a
1124 * status expression.
1125 */
1126 if (rule->expect.status_expr) {
1127 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001128 rule->expect.status_expr, SMP_T_STR);
1129
1130 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1131 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001132 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001133 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001134
1135 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001136}
1137
Christopher Faulet61cc8522020-04-20 14:54:42 +02001138/* Builds the server state header used by HTTP health-checks */
1139static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001140{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001141 int sv_state;
1142 int ratio;
1143 char addr[46];
1144 char port[6];
1145 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1146 "UP %d/%d", "UP",
1147 "NOLB %d/%d", "NOLB",
1148 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001149
Christopher Faulet61cc8522020-04-20 14:54:42 +02001150 if (!(s->check.state & CHK_ST_ENABLED))
1151 sv_state = 6;
1152 else if (s->cur_state != SRV_ST_STOPPED) {
1153 if (s->check.health == s->check.rise + s->check.fall - 1)
1154 sv_state = 3; /* UP */
1155 else
1156 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001157
Christopher Faulet61cc8522020-04-20 14:54:42 +02001158 if (s->cur_state == SRV_ST_STOPPING)
1159 sv_state += 2;
1160 } else {
1161 if (s->check.health)
1162 sv_state = 1; /* going up */
1163 else
1164 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001165 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001166
Christopher Faulet61cc8522020-04-20 14:54:42 +02001167 chunk_appendf(buf, srv_hlt_st[sv_state],
1168 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1169 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001170
Christopher Faulet61cc8522020-04-20 14:54:42 +02001171 addr_to_str(&s->addr, addr, sizeof(addr));
1172 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1173 snprintf(port, sizeof(port), "%u", s->svc_port);
1174 else
1175 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001176
Christopher Faulet61cc8522020-04-20 14:54:42 +02001177 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1178 addr, port, s->proxy->id, s->id,
1179 global.node,
1180 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1181 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1182 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1183 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001184
Christopher Faulet61cc8522020-04-20 14:54:42 +02001185 if ((s->cur_state == SRV_ST_STARTING) &&
1186 now.tv_sec < s->last_change + s->slowstart &&
1187 now.tv_sec >= s->last_change) {
1188 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1189 chunk_appendf(buf, "; throttle=%d%%", ratio);
1190 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001191
Christopher Faulet61cc8522020-04-20 14:54:42 +02001192 return b_data(buf);
1193}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001194
Christopher Faulet61cc8522020-04-20 14:54:42 +02001195/* Internal functions to parse and validate a MySQL packet in the context of an
1196 * expect rule. It start to parse the input buffer at the offset <offset>. If
1197 * <last_read> is set, no more data are expected.
1198 */
1199static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1200 unsigned int offset, int last_read)
1201{
1202 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1203 enum healthcheck_status status;
1204 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001205 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001206 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001207
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001208
Christopher Faulet61cc8522020-04-20 14:54:42 +02001209 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001210 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001211 if (!last_read)
1212 goto wait_more_data;
1213
1214 /* invalid length or truncated response */
1215 status = HCHK_STATUS_L7RSP;
1216 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001217 }
1218
Christopher Faulet61cc8522020-04-20 14:54:42 +02001219 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1220 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1221 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001222
Christopher Faulet61cc8522020-04-20 14:54:42 +02001223 if (b_data(&check->bi) < offset+plen+4) {
1224 if (!last_read)
1225 goto wait_more_data;
1226
1227 /* invalid length or truncated response */
1228 status = HCHK_STATUS_L7RSP;
1229 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001230 }
Simon Horman98637e52014-06-20 12:30:16 +09001231
Christopher Faulet61cc8522020-04-20 14:54:42 +02001232 if (*b_peek(&check->bi, offset+4) == '\xff') {
1233 /* MySQL Error packet always begin with field_count = 0xff */
1234 status = HCHK_STATUS_L7STS;
1235 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1236 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1237 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1238 goto error;
1239 }
Simon Horman98637e52014-06-20 12:30:16 +09001240
Christopher Faulet61cc8522020-04-20 14:54:42 +02001241 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1242 /* Not the last rule, continue */
1243 goto out;
1244 }
Simon Horman98637e52014-06-20 12:30:16 +09001245
Christopher Faulet61cc8522020-04-20 14:54:42 +02001246 /* We set the MySQL Version in description for information purpose
1247 * FIXME : it can be cool to use MySQL Version for other purpose,
1248 * like mark as down old MySQL server.
1249 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001250 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1251 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001252
Christopher Faulet61cc8522020-04-20 14:54:42 +02001253 out:
1254 free_trash_chunk(msg);
1255 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001256
Christopher Faulet61cc8522020-04-20 14:54:42 +02001257 error:
1258 ret = TCPCHK_EVAL_STOP;
1259 check->code = err;
1260 msg = alloc_trash_chunk();
1261 if (msg)
1262 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1263 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1264 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001265
Christopher Faulet61cc8522020-04-20 14:54:42 +02001266 wait_more_data:
1267 ret = TCPCHK_EVAL_WAIT;
1268 goto out;
1269}
Simon Horman98637e52014-06-20 12:30:16 +09001270
Christopher Faulet61cc8522020-04-20 14:54:42 +02001271/* Custom tcp-check expect function to parse and validate the MySQL initial
1272 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1273 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1274 * error occurred.
1275 */
1276static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1277{
1278 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1279}
Simon Horman98637e52014-06-20 12:30:16 +09001280
Christopher Faulet61cc8522020-04-20 14:54:42 +02001281/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1282 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1283 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1284 * an error occurred.
1285 */
1286static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1287{
1288 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001289
Christopher Faulet61cc8522020-04-20 14:54:42 +02001290 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1291 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1292 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001293
Christopher Faulet61cc8522020-04-20 14:54:42 +02001294 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1295}
Simon Horman98637e52014-06-20 12:30:16 +09001296
Christopher Faulet61cc8522020-04-20 14:54:42 +02001297/* Custom tcp-check expect function to parse and validate the LDAP bind response
1298 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1299 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1300 * error occurred.
1301 */
1302static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1303{
1304 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1305 enum healthcheck_status status;
1306 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001307 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001308 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001309
Christopher Faulet61cc8522020-04-20 14:54:42 +02001310 /* Check if the server speaks LDAP (ASN.1/BER)
1311 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1312 * http://tools.ietf.org/html/rfc4511
1313 */
1314 /* size of LDAPMessage */
1315 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001316
Christopher Faulet61cc8522020-04-20 14:54:42 +02001317 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1318 * messageID: 0x02 0x01 0x01: INTEGER 1
1319 * protocolOp: 0x61: bindResponse
1320 */
1321 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1322 status = HCHK_STATUS_L7RSP;
1323 desc = ist("Not LDAPv3 protocol");
1324 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001325 }
Simon Horman98637e52014-06-20 12:30:16 +09001326
Christopher Faulet61cc8522020-04-20 14:54:42 +02001327 /* size of bindResponse */
1328 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001329
Christopher Faulet61cc8522020-04-20 14:54:42 +02001330 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1331 * ldapResult: 0x0a 0x01: ENUMERATION
1332 */
1333 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1334 status = HCHK_STATUS_L7RSP;
1335 desc = ist("Not LDAPv3 protocol");
1336 goto error;
1337 }
Simon Horman98637e52014-06-20 12:30:16 +09001338
Christopher Faulet61cc8522020-04-20 14:54:42 +02001339 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1340 * resultCode
1341 */
1342 check->code = *(b_head(&check->bi) + msglen + 9);
1343 if (check->code) {
1344 status = HCHK_STATUS_L7STS;
1345 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1346 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001347 }
1348
Christopher Faulet1941bab2020-05-05 07:55:50 +02001349 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1350 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001351
Christopher Faulet61cc8522020-04-20 14:54:42 +02001352 out:
1353 free_trash_chunk(msg);
1354 return ret;
1355
1356 error:
1357 ret = TCPCHK_EVAL_STOP;
1358 msg = alloc_trash_chunk();
1359 if (msg)
1360 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1361 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1362 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001363}
1364
Christopher Faulet61cc8522020-04-20 14:54:42 +02001365/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1366 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1367 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001368 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001369static 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 +02001370{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001371 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1372 enum healthcheck_status status;
1373 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001374 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001375 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001376
Willy Tarreaubaaee002006-06-26 02:48:02 +02001377
Christopher Faulet61cc8522020-04-20 14:54:42 +02001378 memcpy(&framesz, b_head(&check->bi), 4);
1379 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001380
Christopher Faulet61cc8522020-04-20 14:54:42 +02001381 if (!last_read && b_data(&check->bi) < (4+framesz))
1382 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001383
Christopher Faulet61cc8522020-04-20 14:54:42 +02001384 memset(b_orig(&trash), 0, b_size(&trash));
1385 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1386 status = HCHK_STATUS_L7RSP;
1387 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1388 goto error;
1389 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001390
Christopher Faulet1941bab2020-05-05 07:55:50 +02001391 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1392 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001393
Christopher Faulet61cc8522020-04-20 14:54:42 +02001394 out:
1395 free_trash_chunk(msg);
1396 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001397
Christopher Faulet61cc8522020-04-20 14:54:42 +02001398 error:
1399 ret = TCPCHK_EVAL_STOP;
1400 msg = alloc_trash_chunk();
1401 if (msg)
1402 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1403 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1404 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406 wait_more_data:
1407 ret = TCPCHK_EVAL_WAIT;
1408 goto out;
1409}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001410
Christopher Faulet61cc8522020-04-20 14:54:42 +02001411/* Custom tcp-check expect function to parse and validate the agent-check
1412 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1413 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1414 */
1415static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1416{
1417 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1418 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1419 const char *hs = NULL; /* health status */
1420 const char *as = NULL; /* admin status */
1421 const char *ps = NULL; /* performance status */
1422 const char *cs = NULL; /* maxconn */
1423 const char *err = NULL; /* first error to report */
1424 const char *wrn = NULL; /* first warning to report */
1425 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001426
Christopher Faulet61cc8522020-04-20 14:54:42 +02001427 /* We're getting an agent check response. The agent could
1428 * have been disabled in the mean time with a long check
1429 * still pending. It is important that we ignore the whole
1430 * response.
1431 */
1432 if (!(check->state & CHK_ST_ENABLED))
1433 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001434
Christopher Faulet61cc8522020-04-20 14:54:42 +02001435 /* The agent supports strings made of a single line ended by the
1436 * first CR ('\r') or LF ('\n'). This line is composed of words
1437 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1438 * line may optionally contained a description of a state change
1439 * after a sharp ('#'), which is only considered if a health state
1440 * is announced.
1441 *
1442 * Words may be composed of :
1443 * - a numeric weight suffixed by the percent character ('%').
1444 * - a health status among "up", "down", "stopped", and "fail".
1445 * - an admin status among "ready", "drain", "maint".
1446 *
1447 * These words may appear in any order. If multiple words of the
1448 * same category appear, the last one wins.
1449 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001450
Christopher Faulet61cc8522020-04-20 14:54:42 +02001451 p = b_head(&check->bi);
1452 while (*p && *p != '\n' && *p != '\r')
1453 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001454
Christopher Faulet61cc8522020-04-20 14:54:42 +02001455 if (!*p) {
1456 if (!last_read)
1457 goto wait_more_data;
1458
1459 /* at least inform the admin that the agent is mis-behaving */
1460 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1461 goto out;
1462 }
1463
1464 *p = 0;
1465 cmd = b_head(&check->bi);
1466
1467 while (*cmd) {
1468 /* look for next word */
1469 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1470 cmd++;
1471 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001472 }
1473
Christopher Faulet61cc8522020-04-20 14:54:42 +02001474 if (*cmd == '#') {
1475 /* this is the beginning of a health status description,
1476 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001477 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001478 cmd++;
1479 while (*cmd == '\t' || *cmd == ' ')
1480 cmd++;
1481 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001482 }
1483
Christopher Faulet61cc8522020-04-20 14:54:42 +02001484 /* find the end of the word so that we have a null-terminated
1485 * word between <cmd> and <p>.
1486 */
1487 p = cmd + 1;
1488 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1489 p++;
1490 if (*p)
1491 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001492
Christopher Faulet61cc8522020-04-20 14:54:42 +02001493 /* first, health statuses */
1494 if (strcasecmp(cmd, "up") == 0) {
1495 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1496 status = HCHK_STATUS_L7OKD;
1497 hs = cmd;
1498 }
1499 else if (strcasecmp(cmd, "down") == 0) {
1500 check->server->check.health = 0;
1501 status = HCHK_STATUS_L7STS;
1502 hs = cmd;
1503 }
1504 else if (strcasecmp(cmd, "stopped") == 0) {
1505 check->server->check.health = 0;
1506 status = HCHK_STATUS_L7STS;
1507 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001508 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001509 else if (strcasecmp(cmd, "fail") == 0) {
1510 check->server->check.health = 0;
1511 status = HCHK_STATUS_L7STS;
1512 hs = cmd;
1513 }
1514 /* admin statuses */
1515 else if (strcasecmp(cmd, "ready") == 0) {
1516 as = cmd;
1517 }
1518 else if (strcasecmp(cmd, "drain") == 0) {
1519 as = cmd;
1520 }
1521 else if (strcasecmp(cmd, "maint") == 0) {
1522 as = cmd;
1523 }
1524 /* try to parse a weight here and keep the last one */
1525 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1526 ps = cmd;
1527 }
1528 /* try to parse a maxconn here */
1529 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1530 cs = cmd;
1531 }
1532 else {
1533 /* keep a copy of the first error */
1534 if (!err)
1535 err = cmd;
1536 }
1537 /* skip to next word */
1538 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001539 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001540 /* here, cmd points either to \0 or to the beginning of a
1541 * description. Skip possible leading spaces.
1542 */
1543 while (*cmd == ' ' || *cmd == '\n')
1544 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001545
Christopher Faulet61cc8522020-04-20 14:54:42 +02001546 /* First, update the admin status so that we avoid sending other
1547 * possibly useless warnings and can also update the health if
1548 * present after going back up.
1549 */
1550 if (as) {
1551 if (strcasecmp(as, "drain") == 0)
1552 srv_adm_set_drain(check->server);
1553 else if (strcasecmp(as, "maint") == 0)
1554 srv_adm_set_maint(check->server);
1555 else
1556 srv_adm_set_ready(check->server);
1557 }
Simon Horman98637e52014-06-20 12:30:16 +09001558
Christopher Faulet61cc8522020-04-20 14:54:42 +02001559 /* now change weights */
1560 if (ps) {
1561 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001562
Christopher Faulet61cc8522020-04-20 14:54:42 +02001563 msg = server_parse_weight_change_request(check->server, ps);
1564 if (!wrn || !*wrn)
1565 wrn = msg;
1566 }
Simon Horman98637e52014-06-20 12:30:16 +09001567
Christopher Faulet61cc8522020-04-20 14:54:42 +02001568 if (cs) {
1569 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001570
Christopher Faulet61cc8522020-04-20 14:54:42 +02001571 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001572
Christopher Faulet61cc8522020-04-20 14:54:42 +02001573 msg = server_parse_maxconn_change_request(check->server, cs);
1574 if (!wrn || !*wrn)
1575 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001576 }
1577
Christopher Faulet61cc8522020-04-20 14:54:42 +02001578 /* and finally health status */
1579 if (hs) {
1580 /* We'll report some of the warnings and errors we have
1581 * here. Down reports are critical, we leave them untouched.
1582 * Lack of report, or report of 'UP' leaves the room for
1583 * ERR first, then WARN.
1584 */
1585 const char *msg = cmd;
1586 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001587
Christopher Faulet61cc8522020-04-20 14:54:42 +02001588 if (!*msg || status == HCHK_STATUS_L7OKD) {
1589 if (err && *err)
1590 msg = err;
1591 else if (wrn && *wrn)
1592 msg = wrn;
1593 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001594
Christopher Faulet61cc8522020-04-20 14:54:42 +02001595 t = get_trash_chunk();
1596 chunk_printf(t, "via agent : %s%s%s%s",
1597 hs, *msg ? " (" : "",
1598 msg, *msg ? ")" : "");
1599 set_server_check_status(check, status, t->area);
1600 }
1601 else if (err && *err) {
1602 /* No status change but we'd like to report something odd.
1603 * Just report the current state and copy the message.
1604 */
1605 chunk_printf(&trash, "agent reports an error : %s", err);
1606 set_server_check_status(check, status/*check->status*/, trash.area);
1607 }
1608 else if (wrn && *wrn) {
1609 /* No status change but we'd like to report something odd.
1610 * Just report the current state and copy the message.
1611 */
1612 chunk_printf(&trash, "agent warns : %s", wrn);
1613 set_server_check_status(check, status/*check->status*/, trash.area);
1614 }
1615 else
1616 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001617
Christopher Faulet61cc8522020-04-20 14:54:42 +02001618 out:
1619 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001620
Christopher Faulet61cc8522020-04-20 14:54:42 +02001621 wait_more_data:
1622 ret = TCPCHK_EVAL_WAIT;
1623 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001624}
1625
Christopher Faulet61cc8522020-04-20 14:54:42 +02001626/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1627 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1628 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001629 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001630static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001631{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001632 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1633 struct tcpcheck_connect *connect = &rule->connect;
1634 struct proxy *proxy = check->proxy;
1635 struct server *s = check->server;
1636 struct task *t = check->task;
1637 struct conn_stream *cs;
1638 struct connection *conn = NULL;
1639 struct protocol *proto;
1640 struct xprt_ops *xprt;
1641 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001642
Christopher Faulet61cc8522020-04-20 14:54:42 +02001643 /* For a connect action we'll create a new connection. We may also have
1644 * to kill a previous one. But we don't want to leave *without* a
1645 * connection if we came here from the connection layer, hence with a
1646 * connection. Thus we'll proceed in the following order :
1647 * 1: close but not release previous connection (handled by the caller)
1648 * 2: try to get a new connection
1649 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001650 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001651
Christopher Faulet61cc8522020-04-20 14:54:42 +02001652 /* 2- prepare new connection */
1653 cs = cs_new(NULL);
1654 if (!cs) {
1655 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1656 tcpcheck_get_step_id(check, rule));
1657 if (rule->comment)
1658 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1659 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1660 ret = TCPCHK_EVAL_STOP;
1661 goto out;
1662 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001663
Christopher Faulet61cc8522020-04-20 14:54:42 +02001664 /* 3- release and replace the old one on success */
1665 if (check->cs) {
1666 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001667 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1668 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001669
1670 /* We may have been scheduled to run, and the I/O handler
1671 * expects to have a cs, so remove the tasklet
1672 */
1673 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1674 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001675 }
1676
Christopher Faulet61cc8522020-04-20 14:54:42 +02001677 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001678
Christopher Faulet61cc8522020-04-20 14:54:42 +02001679 check->cs = cs;
1680 conn = cs->conn;
1681 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001682
Christopher Faulet61cc8522020-04-20 14:54:42 +02001683 /* Maybe there were an older connection we were waiting on */
1684 check->wait_list.events = 0;
1685 conn->target = s ? &s->obj_type : &proxy->obj_type;
1686
1687 /* no client address */
1688 if (!sockaddr_alloc(&conn->dst)) {
1689 status = SF_ERR_RESOURCE;
1690 goto fail_check;
1691 }
1692
1693 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001694 * addr if specified on the server. otherwise, use the server addr (it
1695 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001696 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001697 *conn->dst = (is_addr(&connect->addr)
1698 ? connect->addr
1699 : (is_addr(&check->addr) ? check->addr : s->addr));
1700 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001701
Christopher Faulet61cc8522020-04-20 14:54:42 +02001702 port = 0;
1703 if (!port && connect->port)
1704 port = connect->port;
1705 if (!port && connect->port_expr) {
1706 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001707
Christopher Faulet61cc8522020-04-20 14:54:42 +02001708 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1709 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1710 connect->port_expr, SMP_T_SINT);
1711 if (smp)
1712 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001713 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001714 if (!port && is_inet_addr(&connect->addr))
1715 port = get_host_port(&connect->addr);
1716 if (!port && check->port)
1717 port = check->port;
1718 if (!port && is_inet_addr(&check->addr))
1719 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001720 if (!port) {
1721 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001722 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001723 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001724 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001725
Christopher Faulet61cc8522020-04-20 14:54:42 +02001726 xprt = ((connect->options & TCPCHK_OPT_SSL)
1727 ? xprt_get(XPRT_SSL)
1728 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001729
Christopher Faulet61cc8522020-04-20 14:54:42 +02001730 conn_prepare(conn, proto, xprt);
1731 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001732
Christopher Faulet61cc8522020-04-20 14:54:42 +02001733 status = SF_ERR_INTERNAL;
1734 if (proto && proto->connect) {
1735 struct tcpcheck_rule *next;
1736 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001737
Christopher Faulet61cc8522020-04-20 14:54:42 +02001738 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1739 flags |= CONNECT_HAS_DATA;
Christopher Faulet206368d2020-04-03 14:51:06 +02001740
Christopher Faulet61cc8522020-04-20 14:54:42 +02001741 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
1742 if (!next || next->action != TCPCHK_ACT_EXPECT)
1743 flags |= CONNECT_DELACK_ALWAYS;
1744 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001745 }
1746
Christopher Faulet61cc8522020-04-20 14:54:42 +02001747 if (status != SF_ERR_NONE)
1748 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001749
Christopher Faulet61cc8522020-04-20 14:54:42 +02001750 conn->flags |= CO_FL_PRIVATE;
1751 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001752
Christopher Faulet61cc8522020-04-20 14:54:42 +02001753 /* The mux may be initialized now if there isn't server attached to the
1754 * check (email alerts) or if there is a mux proto specified or if there
1755 * is no alpn.
1756 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001757 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1758 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001759 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001760
Christopher Faulet61cc8522020-04-20 14:54:42 +02001761 if (connect->mux_proto)
1762 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001763 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001764 mux_ops = check->mux_proto->mux;
1765 else {
1766 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1767 ? PROTO_MODE_HTTP
1768 : PROTO_MODE_TCP);
1769
1770 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001771 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001772 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1773 status = SF_ERR_INTERNAL;
1774 goto fail_check;
1775 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001776 }
1777
Christopher Faulet61cc8522020-04-20 14:54:42 +02001778#ifdef USE_OPENSSL
1779 if (connect->sni)
1780 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001781 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 ssl_sock_set_servername(conn, s->check.sni);
1783
1784 if (connect->alpn)
1785 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001786 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001787 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1788#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001789 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001790 conn->send_proxy_ofs = 1;
1791 conn->flags |= CO_FL_SOCKS4;
1792 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001793 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 +02001794 conn->send_proxy_ofs = 1;
1795 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001796 }
1797
Christopher Faulet61cc8522020-04-20 14:54:42 +02001798 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1799 conn->send_proxy_ofs = 1;
1800 conn->flags |= CO_FL_SEND_PROXY;
1801 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001802 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 +02001803 conn->send_proxy_ofs = 1;
1804 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001805 }
1806
Christopher Faulet61cc8522020-04-20 14:54:42 +02001807 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1808 /* Some servers don't like reset on close */
1809 fdtab[cs->conn->handle.fd].linger_risk = 0;
1810 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001811
Christopher Faulet61cc8522020-04-20 14:54:42 +02001812 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1813 if (xprt_add_hs(conn) < 0)
1814 status = SF_ERR_RESOURCE;
1815 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001816
Christopher Faulet61cc8522020-04-20 14:54:42 +02001817 fail_check:
1818 /* It can return one of :
1819 * - SF_ERR_NONE if everything's OK
1820 * - SF_ERR_SRVTO if there are no more servers
1821 * - SF_ERR_SRVCL if the connection was refused by the server
1822 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1823 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1824 * - SF_ERR_INTERNAL for any other purely internal errors
1825 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1826 * Note that we try to prevent the network stack from sending the ACK during the
1827 * connect() when a pure TCP check is used (without PROXY protocol).
1828 */
1829 switch (status) {
1830 case SF_ERR_NONE:
1831 /* we allow up to min(inter, timeout.connect) for a connection
1832 * to establish but only when timeout.check is set as it may be
1833 * to short for a full check otherwise
1834 */
1835 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001836
Christopher Faulet61cc8522020-04-20 14:54:42 +02001837 if (proxy->timeout.check && proxy->timeout.connect) {
1838 int t_con = tick_add(now_ms, proxy->timeout.connect);
1839 t->expire = tick_first(t->expire, t_con);
1840 }
1841 break;
1842 case SF_ERR_SRVTO: /* ETIMEDOUT */
1843 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1844 case SF_ERR_PRXCOND:
1845 case SF_ERR_RESOURCE:
1846 case SF_ERR_INTERNAL:
1847 chk_report_conn_err(check, errno, 0);
1848 ret = TCPCHK_EVAL_STOP;
1849 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001850 }
1851
Christopher Faulet61cc8522020-04-20 14:54:42 +02001852 /* don't do anything until the connection is established */
1853 if (conn->flags & CO_FL_WAIT_XPRT) {
1854 ret = TCPCHK_EVAL_WAIT;
1855 goto out;
1856 }
1857
1858 out:
1859 if (conn && check->result == CHK_RES_FAILED)
1860 conn->flags |= CO_FL_ERROR;
1861 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001862}
1863
Christopher Faulet61cc8522020-04-20 14:54:42 +02001864/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1865 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1866 * TCPCHK_EVAL_STOP if an error occurred.
1867 */
1868static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001869{
1870 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001871 struct tcpcheck_send *send = &rule->send;
1872 struct conn_stream *cs = check->cs;
1873 struct connection *conn = cs_conn(cs);
1874 struct buffer *tmp = NULL;
1875 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001876
Christopher Faulet61cc8522020-04-20 14:54:42 +02001877 /* reset the read & write buffer */
1878 b_reset(&check->bi);
1879 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001880
Christopher Faulet61cc8522020-04-20 14:54:42 +02001881 switch (send->type) {
1882 case TCPCHK_SEND_STRING:
1883 case TCPCHK_SEND_BINARY:
1884 if (istlen(send->data) >= b_size(&check->bo)) {
1885 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1886 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1887 tcpcheck_get_step_id(check, rule));
1888 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1889 ret = TCPCHK_EVAL_STOP;
1890 goto out;
1891 }
1892 b_putist(&check->bo, send->data);
1893 break;
1894 case TCPCHK_SEND_STRING_LF:
1895 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1896 if (!b_data(&check->bo))
1897 goto out;
1898 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001899 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001900 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001901
Christopher Faulet61cc8522020-04-20 14:54:42 +02001902 tmp = alloc_trash_chunk();
1903 if (!tmp)
1904 goto error_lf;
1905 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1906 if (!b_data(tmp))
1907 goto out;
1908 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001909 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001910 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001911 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001912 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001913 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001914 case TCPCHK_SEND_HTTP: {
1915 struct htx_sl *sl;
1916 struct ist meth, uri, vsn, clen, body;
1917 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001918
Christopher Faulet61cc8522020-04-20 14:54:42 +02001919 tmp = alloc_trash_chunk();
1920 if (!tmp)
1921 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001922
Christopher Faulet61cc8522020-04-20 14:54:42 +02001923 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1924 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1925 : http_known_methods[send->http.meth.meth]);
1926 uri = (isttest(send->http.uri) ? send->http.uri : ist("/")); // TODO: handle uri_fmt
1927 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001928
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001929 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1930 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001931 slflags |= HTX_SL_F_VER_11;
1932 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1933 if (!isttest(send->http.body))
1934 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001935
Christopher Faulet61cc8522020-04-20 14:54:42 +02001936 htx = htx_from_buf(&check->bo);
1937 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1938 if (!sl)
1939 goto error_htx;
1940 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001941 if (!http_update_host(htx, sl, uri))
1942 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001943
Christopher Faulet61cc8522020-04-20 14:54:42 +02001944 body = send->http.body; // TODO: handle body_fmt
1945 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001946
Christopher Faulet61cc8522020-04-20 14:54:42 +02001947 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
1948 !htx_add_header(htx, ist("Content-length"), clen))
1949 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001950
Christopher Faulet61cc8522020-04-20 14:54:42 +02001951 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1952 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001953 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001954
Christopher Faulet61cc8522020-04-20 14:54:42 +02001955 list_for_each_entry(hdr, &send->http.hdrs, list) {
1956 chunk_reset(tmp);
1957 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
1958 if (!b_data(tmp))
1959 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02001960 hdr_value = ist2(b_orig(tmp), b_data(tmp));
1961 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001962 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02001963 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
1964 if (!http_update_authority(htx, sl, hdr_value))
1965 goto error_htx;
1966 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001967 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001968
Christopher Faulet61cc8522020-04-20 14:54:42 +02001969 }
1970 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
1971 chunk_reset(tmp);
1972 httpchk_build_status_header(check->server, tmp);
1973 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
1974 goto error_htx;
1975 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001976
Christopher Faulet61cc8522020-04-20 14:54:42 +02001977 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
1978 (istlen(body) && !htx_add_data_atonce(htx, send->http.body)) ||
1979 !htx_add_endof(htx, HTX_BLK_EOM))
1980 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001981
Christopher Faulet61cc8522020-04-20 14:54:42 +02001982 htx_to_buf(htx, &check->bo);
1983 break;
1984 }
1985 case TCPCHK_SEND_UNDEF:
1986 /* Should never happen. */
1987 ret = TCPCHK_EVAL_STOP;
1988 goto out;
1989 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001990
Christopher Faulet6d471212020-04-22 11:09:25 +02001991
1992 if (conn->mux->snd_buf(cs, &check->bo,
1993 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02001994 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001995 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02001996 goto out;
1997 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001998 }
Christopher Faulet6d471212020-04-22 11:09:25 +02001999 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002000 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2001 ret = TCPCHK_EVAL_WAIT;
2002 goto out;
2003 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002004
Christopher Faulet61cc8522020-04-20 14:54:42 +02002005 out:
2006 free_trash_chunk(tmp);
2007 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002008
Christopher Faulet61cc8522020-04-20 14:54:42 +02002009 error_htx:
2010 if (htx) {
2011 htx_reset(htx);
2012 htx_to_buf(htx, &check->bo);
2013 }
2014 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2015 tcpcheck_get_step_id(check, rule));
2016 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2017 ret = TCPCHK_EVAL_STOP;
2018 goto out;
2019
2020 error_lf:
2021 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2022 tcpcheck_get_step_id(check, rule));
2023 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2024 ret = TCPCHK_EVAL_STOP;
2025 goto out;
2026
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002027}
2028
Christopher Faulet61cc8522020-04-20 14:54:42 +02002029/* Try to reveice data before evaluting a tcp-check expect rule. Returns
2030 * TCPCHK_EVAL_WAIT if it is already subcribed on receive events or if nothing
2031 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2032 * TCPCHK_EVAL_STOP if an error occurred.
2033 */
2034static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002035{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 struct conn_stream *cs = check->cs;
2037 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002038 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002039 size_t max, read, cur_read = 0;
2040 int is_empty;
2041 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002042
Christopher Faulet61cc8522020-04-20 14:54:42 +02002043 if (check->wait_list.events & SUB_RETRY_RECV)
2044 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002045
Christopher Faulet61cc8522020-04-20 14:54:42 +02002046 if (cs->flags & CS_FL_EOS)
2047 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002048
Christopher Faulet61cc8522020-04-20 14:54:42 +02002049 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002050
Christopher Faulet61cc8522020-04-20 14:54:42 +02002051 /* prepare to detect if the mux needs more room */
2052 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002053
Christopher Faulet61cc8522020-04-20 14:54:42 +02002054 while ((cs->flags & CS_FL_RCV_MORE) ||
2055 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2056 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2057 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2058 cur_read += read;
2059 if (!read ||
2060 (cs->flags & CS_FL_WANT_ROOM) ||
2061 (--read_poll <= 0) ||
2062 (read < max && read >= global.tune.recv_enough))
2063 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002064 }
2065
Christopher Faulet61cc8522020-04-20 14:54:42 +02002066 end_recv:
2067 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2068 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2069 /* Report network errors only if we got no other data. Otherwise
2070 * we'll let the upper layers decide whether the response is OK
2071 * or not. It is very common that an RST sent by the server is
2072 * reported as an error just after the last data chunk.
2073 */
2074 goto stop;
2075 }
2076 if (!cur_read) {
2077 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2078 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2079 goto wait_more_data;
2080 }
2081 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002082 int status;
2083
Christopher Faulet61cc8522020-04-20 14:54:42 +02002084 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2085 tcpcheck_get_step_id(check, rule));
2086 if (rule->comment)
2087 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002088
2089 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2090 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002091 goto stop;
2092 }
2093 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002094
2095 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002096 return ret;
2097
Christopher Faulet61cc8522020-04-20 14:54:42 +02002098 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002099 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002100 goto out;
2101
2102 wait_more_data:
2103 ret = TCPCHK_EVAL_WAIT;
2104 goto out;
2105}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002106
Christopher Faulet61cc8522020-04-20 14:54:42 +02002107/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2108 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2109 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2110 * error occurred.
2111 */
2112static 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 +02002113{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002114 struct htx *htx = htxbuf(&check->bi);
2115 struct htx_sl *sl;
2116 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002117 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002118 struct tcpcheck_expect *expect = &rule->expect;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002119 struct buffer *msg = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002120 enum healthcheck_status status;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002121 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002122 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002123
Christopher Faulet61cc8522020-04-20 14:54:42 +02002124 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002125
Christopher Faulet61cc8522020-04-20 14:54:42 +02002126 if (htx->flags & HTX_FL_PARSING_ERROR) {
2127 status = HCHK_STATUS_L7RSP;
2128 goto error;
2129 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002130
Christopher Faulet61cc8522020-04-20 14:54:42 +02002131 if (htx_is_empty(htx)) {
2132 if (last_read) {
2133 status = HCHK_STATUS_L7RSP;
2134 goto error;
2135 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002136 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002137 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002138
Christopher Faulet61cc8522020-04-20 14:54:42 +02002139 sl = http_get_stline(htx);
2140 check->code = sl->info.res.status;
2141
2142 if (check->server &&
2143 (check->server->proxy->options & PR_O_DISABLE404) &&
2144 (check->server->next_state != SRV_ST_STOPPED) &&
2145 (check->code == 404)) {
2146 /* 404 may be accepted as "stopping" only if the server was up */
2147 goto out;
2148 }
2149
2150 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2151 /* Make GCC happy ; initialize match to a failure state. */
2152 match = inverse;
2153
2154 switch (expect->type) {
2155 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002156 match = 0;
2157 for (i = 0; i < expect->codes.num; i++) {
2158 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2159 sl->info.res.status <= expect->codes.codes[i][1]) {
2160 match = 1;
2161 break;
2162 }
2163 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002164
2165 /* Set status and description in case of error */
Christopher Faulet1941bab2020-05-05 07:55:50 +02002166 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7STS);
2167 if (LIST_ISEMPTY(&expect->onerror_fmt))
2168 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002169 break;
2170 case TCPCHK_EXPECT_HTTP_REGEX_STATUS:
2171 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2172
2173 /* Set status and description in case of error */
Christopher Faulet1941bab2020-05-05 07:55:50 +02002174 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7STS);
2175 if (LIST_ISEMPTY(&expect->onerror_fmt))
2176 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002177 break;
2178
2179 case TCPCHK_EXPECT_HTTP_BODY:
2180 case TCPCHK_EXPECT_HTTP_REGEX_BODY:
2181 chunk_reset(&trash);
2182 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2183 enum htx_blk_type type = htx_get_blk_type(blk);
2184
2185 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2186 break;
2187 if (type == HTX_BLK_DATA) {
2188 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2189 break;
2190 }
2191 }
2192
2193 if (!b_data(&trash)) {
2194 if (!last_read)
2195 goto wait_more_data;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002196 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2197 if (LIST_ISEMPTY(&expect->onerror_fmt))
2198 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002199 goto error;
2200 }
2201
2202 if (!last_read &&
2203 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
2204 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2205 ret = TCPCHK_EVAL_WAIT;
2206 goto out;
2207 }
2208
2209 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002210 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002211 else
2212 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2213
2214 /* Set status and description in case of error */
Christopher Faulet1941bab2020-05-05 07:55:50 +02002215 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2216 if (LIST_ISEMPTY(&expect->onerror_fmt))
2217 desc = (inverse
2218 ? ist("HTTP check matched unwanted content")
2219 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002220 break;
2221
2222 default:
2223 /* should never happen */
Christopher Faulet1941bab2020-05-05 07:55:50 +02002224 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002225 goto error;
2226 }
2227
Christopher Faulet61cc8522020-04-20 14:54:42 +02002228 /* Wait for more data on mismatch only if no minimum is defined (-1),
2229 * otherwise the absence of match is already conclusive.
2230 */
2231 if (!match && !last_read && (expect->min_recv == -1)) {
2232 ret = TCPCHK_EVAL_WAIT;
2233 goto out;
2234 }
2235
2236 if (!(match ^ inverse))
2237 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002238
2239 out:
2240 free_trash_chunk(msg);
2241 return ret;
2242
2243 error:
2244 ret = TCPCHK_EVAL_STOP;
2245 msg = alloc_trash_chunk();
2246 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002247 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002248 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2249 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002250
2251 wait_more_data:
2252 ret = TCPCHK_EVAL_WAIT;
2253 goto out;
2254}
2255
Christopher Faulet61cc8522020-04-20 14:54:42 +02002256/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2257 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2258 * if an error occurred.
2259 */
2260static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2261{
2262 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2263 struct tcpcheck_expect *expect = &rule->expect;
2264 struct buffer *msg = NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002265 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002266 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002267
Christopher Faulet61cc8522020-04-20 14:54:42 +02002268 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002269
Christopher Faulet61cc8522020-04-20 14:54:42 +02002270 /* The current expect might need more data than the previous one, check again
2271 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002272 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002273 if (!last_read) {
2274 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2275 (b_data(&check->bi) < istlen(expect->data))) {
2276 ret = TCPCHK_EVAL_WAIT;
2277 goto out;
2278 }
2279 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2280 ret = TCPCHK_EVAL_WAIT;
2281 goto out;
2282 }
2283 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002284
Christopher Faulet61cc8522020-04-20 14:54:42 +02002285 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2286 /* Make GCC happy ; initialize match to a failure state. */
2287 match = inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002288
Christopher Faulet61cc8522020-04-20 14:54:42 +02002289 switch (expect->type) {
2290 case TCPCHK_EXPECT_STRING:
2291 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002292 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 +02002293 break;
2294 case TCPCHK_EXPECT_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002295 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 +02002296 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002297
Christopher Faulet61cc8522020-04-20 14:54:42 +02002298 case TCPCHK_EXPECT_REGEX_BINARY:
2299 chunk_reset(&trash);
2300 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002301 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002302 break;
2303 case TCPCHK_EXPECT_CUSTOM:
2304 if (expect->custom)
2305 ret = expect->custom(check, rule, last_read);
2306 goto out;
2307 default:
2308 /* Should never happen. */
2309 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002310 goto out;
2311 }
2312
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002313
Christopher Faulet61cc8522020-04-20 14:54:42 +02002314 /* Wait for more data on mismatch only if no minimum is defined (-1),
2315 * otherwise the absence of match is already conclusive.
2316 */
2317 if (!match && !last_read && (expect->min_recv == -1)) {
2318 ret = TCPCHK_EVAL_WAIT;
2319 goto out;
2320 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002321
Christopher Faulet61cc8522020-04-20 14:54:42 +02002322 /* Result as expected, next rule. */
2323 if (match ^ inverse)
2324 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002325
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002326
Christopher Faulet61cc8522020-04-20 14:54:42 +02002327 /* From this point on, we matched something we did not want, this is an error state. */
2328 ret = TCPCHK_EVAL_STOP;
2329 msg = alloc_trash_chunk();
2330 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002331 tcpcheck_expect_onerror_message(msg, check, rule, match, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002332
2333 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2334 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002335 free_trash_chunk(msg);
2336 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002337
Christopher Faulet61cc8522020-04-20 14:54:42 +02002338 out:
2339 return ret;
2340}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002341
Christopher Faulet61cc8522020-04-20 14:54:42 +02002342/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
2343 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It nevers
2344 * waits.
2345 */
2346static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2347{
2348 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2349 struct act_rule *act_rule;
2350 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002351
Christopher Faulet61cc8522020-04-20 14:54:42 +02002352 act_rule =rule->action_kw.rule;
2353 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2354 if (act_ret != ACT_RET_CONT) {
2355 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2356 tcpcheck_get_step_id(check, rule));
2357 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2358 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002359 }
2360
Christopher Faulet61cc8522020-04-20 14:54:42 +02002361 return ret;
2362}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002363
Christopher Faulet61cc8522020-04-20 14:54:42 +02002364/* Executes a tcp-check ruleset. Note that this is called both from the
2365 * connection's wake() callback and from the check scheduling task. It returns
2366 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2367 * presenting the risk of an fd replacement.
2368 *
2369 * Please do NOT place any return statement in this function and only leave
2370 * via the out_end_tcpcheck label after setting retcode.
2371 */
2372static int tcpcheck_main(struct check *check)
2373{
2374 struct tcpcheck_rule *rule;
2375 struct conn_stream *cs = check->cs;
2376 struct connection *conn = cs_conn(cs);
2377 int must_read = 1, last_read = 0;
2378 int ret, retcode = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002379
Christopher Faulet61cc8522020-04-20 14:54:42 +02002380 /* here, we know that the check is complete or that it failed */
2381 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002382 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002383
Christopher Faulet61cc8522020-04-20 14:54:42 +02002384 /* 1- check for connection error, if any */
2385 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2386 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002387
Christopher Faulet61cc8522020-04-20 14:54:42 +02002388 /* 2- check if we are waiting for the connection establishment. It only
2389 * happens during TCPCHK_ACT_CONNECT. */
2390 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
2391 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
2392 if (conn && (conn->flags & CO_FL_WAIT_XPRT)) {
2393 if (rule->action == TCPCHK_ACT_SEND)
2394 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2395 else if (rule->action == TCPCHK_ACT_EXPECT)
2396 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2397 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002398 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002399 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002400
2401 /* 3- check for pending outgoing data. It only happens during
2402 * TCPCHK_ACT_SEND. */
2403 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
2404 if (conn && b_data(&check->bo)) {
Christopher Faulet6d471212020-04-22 11:09:25 +02002405 ret = conn->mux->snd_buf(cs, &check->bo,
2406 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002407 if (ret <= 0) {
2408 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2409 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002410 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002411 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002412 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2413 goto out;
2414 }
2415 }
2416 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002417 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002418
Christopher Faulet61cc8522020-04-20 14:54:42 +02002419 /* 4- check if a rule must be resume. It happens if check->current_step
2420 * is defined. */
2421 else if (check->current_step)
2422 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002423
Christopher Faulet61cc8522020-04-20 14:54:42 +02002424 /* 5- It is the first evaluation. We must create a session and preset
2425 * tcp-check variables */
2426 else {
2427 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002428
Christopher Faulet61cc8522020-04-20 14:54:42 +02002429 /* First evaluation, create a session */
2430 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2431 if (!check->sess) {
2432 chunk_printf(&trash, "TCPCHK error allocating check session");
2433 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2434 goto out_end_tcpcheck;
2435 }
2436 vars_init(&check->vars, SCOPE_CHECK);
2437 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002438
Christopher Faulet61cc8522020-04-20 14:54:42 +02002439 /* Preset tcp-check variables */
2440 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2441 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002442
Christopher Faulet61cc8522020-04-20 14:54:42 +02002443 memset(&smp, 0, sizeof(smp));
2444 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2445 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002446 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002447 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002448 }
2449
Christopher Faulet61cc8522020-04-20 14:54:42 +02002450 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002451
Christopher Faulet61cc8522020-04-20 14:54:42 +02002452 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
2453 enum tcpcheck_eval_ret eval_ret;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002454
Christopher Faulet61cc8522020-04-20 14:54:42 +02002455 check->code = 0;
2456 switch (rule->action) {
2457 case TCPCHK_ACT_CONNECT:
2458 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002459
Christopher Faulet61cc8522020-04-20 14:54:42 +02002460 /* close but not release yet previous connection */
2461 if (check->cs) {
2462 cs_close(check->cs);
2463 retcode = -1; /* do not reuse the fd in the caller! */
2464 }
2465 eval_ret = tcpcheck_eval_connect(check, rule);
2466 must_read = 1; last_read = 0;
2467 break;
2468 case TCPCHK_ACT_SEND:
2469 check->current_step = rule;
2470 eval_ret = tcpcheck_eval_send(check, rule);
2471 must_read = 1;
2472 break;
2473 case TCPCHK_ACT_EXPECT:
2474 check->current_step = rule;
2475 if (must_read) {
2476 if (check->proxy->timeout.check)
2477 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002478
Christopher Faulet61cc8522020-04-20 14:54:42 +02002479 eval_ret = tcpcheck_eval_recv(check, rule);
2480 if (eval_ret == TCPCHK_EVAL_STOP)
2481 goto out_end_tcpcheck;
2482 else if (eval_ret == TCPCHK_EVAL_WAIT)
2483 goto out;
2484 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2485 must_read = 0;
2486 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002487
Christopher Faulet61cc8522020-04-20 14:54:42 +02002488 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2489 ? tcpcheck_eval_expect_http(check, rule, last_read)
2490 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002491
Christopher Faulet61cc8522020-04-20 14:54:42 +02002492 if (eval_ret == TCPCHK_EVAL_WAIT) {
2493 check->current_step = rule->expect.head;
2494 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2495 }
2496 break;
2497 case TCPCHK_ACT_ACTION_KW:
2498 /* Don't update the current step */
2499 eval_ret = tcpcheck_eval_action_kw(check, rule);
2500 break;
2501 default:
2502 /* Otherwise, just go to the next one and don't update
2503 * the current step
2504 */
2505 eval_ret = TCPCHK_EVAL_CONTINUE;
2506 break;
2507 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002508
Christopher Faulet61cc8522020-04-20 14:54:42 +02002509 switch (eval_ret) {
2510 case TCPCHK_EVAL_CONTINUE:
2511 break;
2512 case TCPCHK_EVAL_WAIT:
2513 goto out;
2514 case TCPCHK_EVAL_STOP:
2515 goto out_end_tcpcheck;
2516 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002517 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002518
Christopher Faulet61cc8522020-04-20 14:54:42 +02002519 /* All rules was evaluated */
2520 if (check->current_step) {
2521 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002522
Christopher Faulet61cc8522020-04-20 14:54:42 +02002523 if (rule->action == TCPCHK_ACT_EXPECT) {
2524 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002525 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002526
Christopher Faulet61cc8522020-04-20 14:54:42 +02002527 if (check->server &&
2528 (check->server->proxy->options & PR_O_DISABLE404) &&
2529 (check->server->next_state != SRV_ST_STOPPED) &&
2530 (check->code == 404)) {
2531 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2532 goto out_end_tcpcheck;
2533 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002534
Christopher Faulet61cc8522020-04-20 14:54:42 +02002535 msg = alloc_trash_chunk();
2536 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002537 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002538 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2539 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002540 free_trash_chunk(msg);
2541 }
2542 else if (rule->action == TCPCHK_ACT_CONNECT) {
2543 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002544 enum healthcheck_status status = HCHK_STATUS_L4OK;
2545#ifdef USE_OPENSSL
2546 if (conn && ssl_sock_is_ssl(conn))
2547 status = HCHK_STATUS_L6OK;
2548#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002549 set_server_check_status(check, status, msg);
2550 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002551 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002552 else
2553 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002554
Christopher Faulet61cc8522020-04-20 14:54:42 +02002555 out_end_tcpcheck:
2556 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2557 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002558
Christopher Faulet61cc8522020-04-20 14:54:42 +02002559 out:
2560 return retcode;
2561}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002562
Christopher Faulet14cd3162020-04-16 14:50:06 +02002563
Christopher Faulet61cc8522020-04-20 14:54:42 +02002564/**************************************************************************/
2565/************** Health-checks based on an external process ****************/
2566/**************************************************************************/
2567static struct list pid_list = LIST_HEAD_INIT(pid_list);
2568static struct pool_head *pool_head_pid_list;
2569__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002570
Christopher Faulet61cc8522020-04-20 14:54:42 +02002571struct extcheck_env {
2572 char *name; /* environment variable name */
2573 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2574};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002575
Christopher Faulet61cc8522020-04-20 14:54:42 +02002576/* environment variables memory requirement for different types of data */
2577#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2578 * such environment variables are not updatable. */
2579#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2580#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2581#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002582
Christopher Faulet61cc8522020-04-20 14:54:42 +02002583/* external checks environment variables */
2584enum {
2585 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002586
Christopher Faulet61cc8522020-04-20 14:54:42 +02002587 /* Proxy specific environment variables */
2588 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2589 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2590 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2591 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002592
Christopher Faulet61cc8522020-04-20 14:54:42 +02002593 /* Server specific environment variables */
2594 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2595 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2596 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2597 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2598 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2599 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002600
Christopher Faulet61cc8522020-04-20 14:54:42 +02002601 EXTCHK_SIZE
2602};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002603
Christopher Faulet61cc8522020-04-20 14:54:42 +02002604const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2605 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2606 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2607 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2608 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2609 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2610 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2611 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2612 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2613 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2614 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2615 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2616};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002617
Christopher Faulet61cc8522020-04-20 14:54:42 +02002618void block_sigchld(void)
2619{
2620 sigset_t set;
2621 sigemptyset(&set);
2622 sigaddset(&set, SIGCHLD);
2623 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2624}
Willy Tarreaube373152018-09-06 11:45:30 +02002625
Christopher Faulet61cc8522020-04-20 14:54:42 +02002626void unblock_sigchld(void)
2627{
2628 sigset_t set;
2629 sigemptyset(&set);
2630 sigaddset(&set, SIGCHLD);
2631 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002632}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002633
Christopher Faulet61cc8522020-04-20 14:54:42 +02002634static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002635{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002636 struct pid_list *elem;
2637 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002638
Christopher Faulet61cc8522020-04-20 14:54:42 +02002639 elem = pool_alloc(pool_head_pid_list);
2640 if (!elem)
2641 return NULL;
2642 elem->pid = pid;
2643 elem->t = t;
2644 elem->exited = 0;
2645 check->curpid = elem;
2646 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002647
Christopher Faulet61cc8522020-04-20 14:54:42 +02002648 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2649 LIST_ADD(&pid_list, &elem->list);
2650 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002651
Christopher Faulet61cc8522020-04-20 14:54:42 +02002652 return elem;
2653}
Christopher Faulete5870d82020-04-15 11:32:03 +02002654
Christopher Faulet61cc8522020-04-20 14:54:42 +02002655static void pid_list_del(struct pid_list *elem)
2656{
2657 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002658
Christopher Faulet61cc8522020-04-20 14:54:42 +02002659 if (!elem)
2660 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002661
Christopher Faulet61cc8522020-04-20 14:54:42 +02002662 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2663 LIST_DEL(&elem->list);
2664 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002665
Christopher Faulet61cc8522020-04-20 14:54:42 +02002666 if (!elem->exited)
2667 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002668
Christopher Faulet61cc8522020-04-20 14:54:42 +02002669 check = elem->t->context;
2670 check->curpid = NULL;
2671 pool_free(pool_head_pid_list, elem);
2672}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002673
Christopher Faulet61cc8522020-04-20 14:54:42 +02002674/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2675static void pid_list_expire(pid_t pid, int status)
2676{
2677 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002678
Christopher Faulet61cc8522020-04-20 14:54:42 +02002679 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2680 list_for_each_entry(elem, &pid_list, list) {
2681 if (elem->pid == pid) {
2682 elem->t->expire = now_ms;
2683 elem->status = status;
2684 elem->exited = 1;
2685 task_wakeup(elem->t, TASK_WOKEN_IO);
2686 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002687 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002688 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002689 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2690}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002691
Christopher Faulet61cc8522020-04-20 14:54:42 +02002692static void sigchld_handler(struct sig_handler *sh)
2693{
2694 pid_t pid;
2695 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002696
Christopher Faulet61cc8522020-04-20 14:54:42 +02002697 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2698 pid_list_expire(pid, status);
2699}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002700
Christopher Faulet61cc8522020-04-20 14:54:42 +02002701static int init_pid_list(void)
2702{
2703 if (pool_head_pid_list != NULL)
2704 /* Nothing to do */
2705 return 0;
2706
2707 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2708 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2709 strerror(errno));
2710 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002711 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002712
Christopher Faulet61cc8522020-04-20 14:54:42 +02002713 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2714 if (pool_head_pid_list == NULL) {
2715 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2716 strerror(errno));
2717 return 1;
2718 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002719
Christopher Faulet61cc8522020-04-20 14:54:42 +02002720 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002721}
2722
Christopher Faulet61cc8522020-04-20 14:54:42 +02002723/* helper macro to set an environment variable and jump to a specific label on failure. */
2724#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002725
Christopher Faulet61cc8522020-04-20 14:54:42 +02002726/*
2727 * helper function to allocate enough memory to store an environment variable.
2728 * It will also check that the environment variable is updatable, and silently
2729 * fail if not.
2730 */
2731static int extchk_setenv(struct check *check, int idx, const char *value)
2732{
2733 int len, ret;
2734 char *envname;
2735 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002736
Christopher Faulet61cc8522020-04-20 14:54:42 +02002737 if (idx < 0 || idx >= EXTCHK_SIZE) {
2738 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
2739 return 1;
2740 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002741
Christopher Faulet61cc8522020-04-20 14:54:42 +02002742 envname = extcheck_envs[idx].name;
2743 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002744
Christopher Faulet61cc8522020-04-20 14:54:42 +02002745 /* Check if the environment variable is already set, and silently reject
2746 * the update if this one is not updatable. */
2747 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
2748 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002749
Christopher Faulet61cc8522020-04-20 14:54:42 +02002750 /* Instead of sending NOT_USED, sending an empty value is preferable */
2751 if (strcmp(value, "NOT_USED") == 0) {
2752 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02002753 }
2754
Christopher Faulet61cc8522020-04-20 14:54:42 +02002755 len = strlen(envname) + 1;
2756 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
2757 len += strlen(value);
2758 else
2759 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002760
Christopher Faulet61cc8522020-04-20 14:54:42 +02002761 if (!check->envp[idx])
2762 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02002763
Christopher Faulet61cc8522020-04-20 14:54:42 +02002764 if (!check->envp[idx]) {
2765 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
2766 return 1;
2767 }
2768 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
2769 if (ret < 0) {
2770 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
2771 return 1;
2772 }
2773 else if (ret > len) {
2774 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
2775 return 1;
2776 }
2777 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002778}
2779
Christopher Faulet61cc8522020-04-20 14:54:42 +02002780static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02002781{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002782 struct server *s = check->server;
2783 struct proxy *px = s->proxy;
2784 struct listener *listener = NULL, *l;
2785 int i;
2786 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
2787 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02002788
Christopher Faulet61cc8522020-04-20 14:54:42 +02002789 list_for_each_entry(l, &px->conf.listeners, by_fe)
2790 /* Use the first INET, INET6 or UNIX listener */
2791 if (l->addr.ss_family == AF_INET ||
2792 l->addr.ss_family == AF_INET6 ||
2793 l->addr.ss_family == AF_UNIX) {
2794 listener = l;
2795 break;
2796 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002797
Christopher Faulet61cc8522020-04-20 14:54:42 +02002798 check->curpid = NULL;
2799 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
2800 if (!check->envp) {
2801 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
2802 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02002803 }
2804
Christopher Faulet61cc8522020-04-20 14:54:42 +02002805 check->argv = calloc(6, sizeof(char *));
2806 if (!check->argv) {
2807 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
2808 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002809 }
2810
Christopher Faulet61cc8522020-04-20 14:54:42 +02002811 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02002812
Christopher Faulet61cc8522020-04-20 14:54:42 +02002813 if (!listener) {
2814 check->argv[1] = strdup("NOT_USED");
2815 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02002816 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002817 else if (listener->addr.ss_family == AF_INET ||
2818 listener->addr.ss_family == AF_INET6) {
2819 addr_to_str(&listener->addr, buf, sizeof(buf));
2820 check->argv[1] = strdup(buf);
2821 port_to_str(&listener->addr, buf, sizeof(buf));
2822 check->argv[2] = strdup(buf);
2823 }
2824 else if (listener->addr.ss_family == AF_UNIX) {
2825 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02002826
Christopher Faulet61cc8522020-04-20 14:54:42 +02002827 un = (struct sockaddr_un *)&listener->addr;
2828 check->argv[1] = strdup(un->sun_path);
2829 check->argv[2] = strdup("NOT_USED");
2830 }
2831 else {
2832 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
2833 goto err;
2834 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002835
Christopher Faulet61cc8522020-04-20 14:54:42 +02002836 if (!check->argv[1] || !check->argv[2]) {
2837 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
2838 goto err;
2839 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002840
Christopher Faulet61cc8522020-04-20 14:54:42 +02002841 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
2842 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
2843 if (!check->argv[3] || !check->argv[4]) {
2844 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
2845 goto err;
2846 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002847
Christopher Faulet61cc8522020-04-20 14:54:42 +02002848 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
2849 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
2850 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02002851
Christopher Faulet61cc8522020-04-20 14:54:42 +02002852 for (i = 0; i < 5; i++) {
2853 if (!check->argv[i]) {
2854 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
2855 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02002856 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002857 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002858
Christopher Faulet61cc8522020-04-20 14:54:42 +02002859 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
2860 /* Add proxy environment variables */
2861 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
2862 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
2863 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
2864 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
2865 /* Add server environment variables */
2866 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
2867 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
2868 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
2869 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
2870 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
2871 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02002872
Christopher Faulet61cc8522020-04-20 14:54:42 +02002873 /* Ensure that we don't leave any hole in check->envp */
2874 for (i = 0; i < EXTCHK_SIZE; i++)
2875 if (!check->envp[i])
2876 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02002877
Christopher Faulet61cc8522020-04-20 14:54:42 +02002878 return 1;
2879err:
2880 if (check->envp) {
2881 for (i = 0; i < EXTCHK_SIZE; i++)
2882 free(check->envp[i]);
2883 free(check->envp);
2884 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02002885 }
2886
Christopher Faulet61cc8522020-04-20 14:54:42 +02002887 if (check->argv) {
2888 for (i = 1; i < 5; i++)
2889 free(check->argv[i]);
2890 free(check->argv);
2891 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02002892 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002893 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002894}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01002895
Christopher Faulet61cc8522020-04-20 14:54:42 +02002896/*
2897 * establish a server health-check that makes use of a process.
2898 *
2899 * It can return one of :
2900 * - SF_ERR_NONE if everything's OK
2901 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
2902 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
2903 *
2904 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002905 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002906static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002907{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002908 char buf[256];
2909 struct check *check = t->context;
2910 struct server *s = check->server;
2911 struct proxy *px = s->proxy;
2912 int status;
2913 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002914
Christopher Faulet61cc8522020-04-20 14:54:42 +02002915 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02002916
Christopher Faulet61cc8522020-04-20 14:54:42 +02002917 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02002918
Christopher Faulet61cc8522020-04-20 14:54:42 +02002919 pid = fork();
2920 if (pid < 0) {
2921 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
2922 (global.tune.options & GTUNE_INSECURE_FORK) ?
2923 "" : " (likely caused by missing 'insecure-fork-wanted')",
2924 strerror(errno));
2925 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002926 goto out;
2927 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002928 if (pid == 0) {
2929 /* Child */
2930 extern char **environ;
2931 struct rlimit limit;
2932 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01002933
Christopher Faulet61cc8522020-04-20 14:54:42 +02002934 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
2935 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002936
Christopher Faulet61cc8522020-04-20 14:54:42 +02002937 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002938
Christopher Faulet61cc8522020-04-20 14:54:42 +02002939 /* restore the initial FD limits */
2940 limit.rlim_cur = rlim_fd_cur_at_boot;
2941 limit.rlim_max = rlim_fd_max_at_boot;
2942 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
2943 getrlimit(RLIMIT_NOFILE, &limit);
2944 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
2945 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
2946 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
2947 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002948
Christopher Faulet61cc8522020-04-20 14:54:42 +02002949 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01002950
Christopher Faulet61cc8522020-04-20 14:54:42 +02002951 /* Update some environment variables and command args: curconn, server addr and server port */
2952 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002953
Christopher Faulet61cc8522020-04-20 14:54:42 +02002954 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
2955 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002956
Christopher Faulet61cc8522020-04-20 14:54:42 +02002957 *check->argv[4] = 0;
2958 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
2959 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
2960 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01002961
Christopher Faulet61cc8522020-04-20 14:54:42 +02002962 haproxy_unblock_signals();
2963 execvp(px->check_command, check->argv);
2964 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
2965 strerror(errno));
2966 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01002967 }
2968
Christopher Faulet61cc8522020-04-20 14:54:42 +02002969 /* Parent */
2970 if (check->result == CHK_RES_UNKNOWN) {
2971 if (pid_list_add(pid, t) != NULL) {
2972 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
2973
2974 if (px->timeout.check && px->timeout.connect) {
2975 int t_con = tick_add(now_ms, px->timeout.connect);
2976 t->expire = tick_first(t->expire, t_con);
2977 }
2978 status = SF_ERR_NONE;
2979 goto out;
2980 }
2981 else {
2982 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
2983 }
2984 kill(pid, SIGTERM); /* process creation error */
2985 }
2986 else
2987 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
2988
2989out:
2990 unblock_sigchld();
2991 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01002992}
2993
Christopher Faulet61cc8522020-04-20 14:54:42 +02002994/*
2995 * manages a server health-check that uses an external process. Returns
2996 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002997 *
2998 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02002999 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003000 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003001static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003002{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003003 struct check *check = context;
3004 struct server *s = check->server;
3005 int rv;
3006 int ret;
3007 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003008
Christopher Faulet61cc8522020-04-20 14:54:42 +02003009 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3010 if (!(check->state & CHK_ST_INPROGRESS)) {
3011 /* no check currently running */
3012 if (!expired) /* woke up too early */
3013 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003014
Christopher Faulet61cc8522020-04-20 14:54:42 +02003015 /* we don't send any health-checks when the proxy is
3016 * stopped, the server should not be checked or the check
3017 * is disabled.
3018 */
3019 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3020 s->proxy->state == PR_STSTOPPED)
3021 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003022
Christopher Faulet61cc8522020-04-20 14:54:42 +02003023 /* we'll initiate a new check */
3024 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003025
Christopher Faulet61cc8522020-04-20 14:54:42 +02003026 check->state |= CHK_ST_INPROGRESS;
3027
3028 ret = connect_proc_chk(t);
3029 if (ret == SF_ERR_NONE) {
3030 /* the process was forked, we allow up to min(inter,
3031 * timeout.connect) for it to report its status, but
3032 * only when timeout.check is set as it may be to short
3033 * for a full check otherwise.
3034 */
3035 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3036
3037 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3038 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3039 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003040 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003041 task_set_affinity(t, tid_bit);
3042 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003043 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003044
Christopher Faulet61cc8522020-04-20 14:54:42 +02003045 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003046
Christopher Faulet61cc8522020-04-20 14:54:42 +02003047 check->state &= ~CHK_ST_INPROGRESS;
3048 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003049
Christopher Faulet61cc8522020-04-20 14:54:42 +02003050 /* we allow up to min(inter, timeout.connect) for a connection
3051 * to establish but only when timeout.check is set
3052 * as it may be to short for a full check otherwise
3053 */
3054 while (tick_is_expired(t->expire, now_ms)) {
3055 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003056
Christopher Faulet61cc8522020-04-20 14:54:42 +02003057 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3058 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003059
Christopher Faulet61cc8522020-04-20 14:54:42 +02003060 if (s->proxy->timeout.check)
3061 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003062 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003063 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003064 else {
3065 /* there was a test running.
3066 * First, let's check whether there was an uncaught error,
3067 * which can happen on connect timeout or error.
3068 */
3069 if (check->result == CHK_RES_UNKNOWN) {
3070 /* good connection is enough for pure TCP check */
3071 struct pid_list *elem = check->curpid;
3072 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003073
Christopher Faulet61cc8522020-04-20 14:54:42 +02003074 if (elem->exited) {
3075 status = elem->status; /* Save in case the process exits between use below */
3076 if (!WIFEXITED(status))
3077 check->code = -1;
3078 else
3079 check->code = WEXITSTATUS(status);
3080 if (!WIFEXITED(status) || WEXITSTATUS(status))
3081 status = HCHK_STATUS_PROCERR;
3082 else
3083 status = HCHK_STATUS_PROCOK;
3084 } else if (expired) {
3085 status = HCHK_STATUS_PROCTOUT;
3086 ha_warning("kill %d\n", (int)elem->pid);
3087 kill(elem->pid, SIGTERM);
3088 }
3089 set_server_check_status(check, status, NULL);
3090 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003091
Christopher Faulet61cc8522020-04-20 14:54:42 +02003092 if (check->result == CHK_RES_FAILED) {
3093 /* a failure or timeout detected */
3094 check_notify_failure(check);
3095 }
3096 else if (check->result == CHK_RES_CONDPASS) {
3097 /* check is OK but asks for stopping mode */
3098 check_notify_stopping(check);
3099 }
3100 else if (check->result == CHK_RES_PASSED) {
3101 /* a success was detected */
3102 check_notify_success(check);
3103 }
3104 task_set_affinity(t, 1);
3105 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003106
Christopher Faulet61cc8522020-04-20 14:54:42 +02003107 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003108
Christopher Faulet61cc8522020-04-20 14:54:42 +02003109 rv = 0;
3110 if (global.spread_checks > 0) {
3111 rv = srv_getinter(check) * global.spread_checks / 100;
3112 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3113 }
3114 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3115 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003116
Christopher Faulet61cc8522020-04-20 14:54:42 +02003117 reschedule:
3118 while (tick_is_expired(t->expire, now_ms))
3119 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003120
Christopher Faulet61cc8522020-04-20 14:54:42 +02003121 out_unlock:
3122 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3123 return t;
3124}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003125
Baptiste Assmann248f1172018-03-01 21:49:01 +01003126
Christopher Faulet61cc8522020-04-20 14:54:42 +02003127/**************************************************************************/
3128/***************** Health-checks based on connections *********************/
3129/**************************************************************************/
3130/* This function is used only for server health-checks. It handles connection
3131 * status updates including errors. If necessary, it wakes the check task up.
3132 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3133 * connection (eg: reconnect). It relies on tcpcheck_main().
3134 */
3135static int wake_srv_chk(struct conn_stream *cs)
3136{
3137 struct connection *conn = cs->conn;
3138 struct check *check = cs->data;
3139 struct email_alertq *q = container_of(check, typeof(*q), check);
3140 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003141
Christopher Faulet61cc8522020-04-20 14:54:42 +02003142 if (check->server)
3143 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3144 else
3145 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003146
Christopher Faulet61cc8522020-04-20 14:54:42 +02003147 /* we may have to make progress on the TCP checks */
3148 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003149
Christopher Faulet61cc8522020-04-20 14:54:42 +02003150 cs = check->cs;
3151 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003152
Christopher Faulet61cc8522020-04-20 14:54:42 +02003153 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3154 /* We may get error reports bypassing the I/O handlers, typically
3155 * the case when sending a pure TCP check which fails, then the I/O
3156 * handlers above are not called. This is completely handled by the
3157 * main processing task so let's simply wake it up. If we get here,
3158 * we expect errno to still be valid.
3159 */
3160 chk_report_conn_err(check, errno, 0);
3161 task_wakeup(check->task, TASK_WOKEN_IO);
3162 }
3163
3164 if (check->result != CHK_RES_UNKNOWN) {
3165 /* Check complete or aborted. If connection not yet closed do it
3166 * now and wake the check task up to be sure the result is
3167 * handled ASAP. */
3168 conn_sock_drain(conn);
3169 cs_close(cs);
3170 ret = -1;
3171 /* We may have been scheduled to run, and the
3172 * I/O handler expects to have a cs, so remove
3173 * the tasklet
3174 */
3175 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3176 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003177 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003178
3179 if (check->server)
3180 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003181 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003182 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003183
Christopher Faulet61cc8522020-04-20 14:54:42 +02003184 /* if a connection got replaced, we must absolutely prevent the connection
3185 * handler from touching its fd, and perform the FD polling updates ourselves
3186 */
3187 if (ret < 0)
3188 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003189
Christopher Faulet61cc8522020-04-20 14:54:42 +02003190 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003191}
3192
Christopher Faulet61cc8522020-04-20 14:54:42 +02003193/* This function checks if any I/O is wanted, and if so, attempts to do so */
3194static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003195{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003196 struct check *check = ctx;
3197 struct conn_stream *cs = check->cs;
3198 struct email_alertq *q = container_of(check, typeof(*q), check);
3199 int ret = 0;
Simon Hormanb1900d52015-01-30 11:22:54 +09003200
Christopher Faulet61cc8522020-04-20 14:54:42 +02003201 if (!(check->wait_list.events & SUB_RETRY_SEND))
3202 ret = wake_srv_chk(cs);
3203 if (ret == 0 && !(check->wait_list.events & SUB_RETRY_RECV)) {
3204 if (check->server)
3205 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3206 else
3207 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Hormanb1900d52015-01-30 11:22:54 +09003208
Christopher Faulet61cc8522020-04-20 14:54:42 +02003209 if (unlikely(check->result == CHK_RES_FAILED)) {
3210 /* collect possible new errors */
3211 if (cs->conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
3212 chk_report_conn_err(check, 0, 0);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003213
Christopher Faulet61cc8522020-04-20 14:54:42 +02003214 /* Reset the check buffer... */
3215 b_reset(&check->bi);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003216
Christopher Faulet61cc8522020-04-20 14:54:42 +02003217 /* Close the connection... We still attempt to nicely close if,
3218 * for instance, SSL needs to send a "close notify." Later, we perform
3219 * a hard close and reset the connection if some data are pending,
3220 * otherwise we end up with many TIME_WAITs and eat all the source port
3221 * range quickly. To avoid sending RSTs all the time, we first try to
3222 * drain pending data.
3223 */
3224 /* Call cs_shutr() first, to add the CO_FL_SOCK_RD_SH flag on the
3225 * connection, to make sure cs_shutw() will not lead to a shutdown()
3226 * that would provoke TIME_WAITs.
3227 */
3228 cs_shutr(cs, CS_SHR_DRAIN);
3229 cs_shutw(cs, CS_SHW_NORMAL);
Simon Hormanb1900d52015-01-30 11:22:54 +09003230
Christopher Faulet61cc8522020-04-20 14:54:42 +02003231 /* OK, let's not stay here forever */
3232 if (check->result == CHK_RES_FAILED)
3233 cs->conn->flags |= CO_FL_ERROR;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01003234
Christopher Faulet61cc8522020-04-20 14:54:42 +02003235 task_wakeup(t, TASK_WOKEN_IO);
3236 }
3237
3238 if (check->server)
3239 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3240 else
3241 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet23d86d12018-01-25 11:36:35 +01003242 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003243 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003244}
3245
Christopher Faulet61cc8522020-04-20 14:54:42 +02003246/* manages a server health-check that uses a connection. Returns
3247 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3248 *
3249 * Please do NOT place any return statement in this function and only leave
3250 * via the out_unlock label.
3251 */
3252static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003253{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003254 struct check *check = context;
3255 struct proxy *proxy = check->proxy;
3256 struct conn_stream *cs = check->cs;
3257 struct connection *conn = cs_conn(cs);
3258 int rv;
3259 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003260
Christopher Faulet61cc8522020-04-20 14:54:42 +02003261 if (check->server)
3262 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3263 if (!(check->state & CHK_ST_INPROGRESS)) {
3264 /* no check currently running */
3265 if (!expired) /* woke up too early */
3266 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003267
Christopher Faulet61cc8522020-04-20 14:54:42 +02003268 /* we don't send any health-checks when the proxy is
3269 * stopped, the server should not be checked or the check
3270 * is disabled.
3271 */
3272 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3273 proxy->state == PR_STSTOPPED)
3274 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003275
Christopher Faulet61cc8522020-04-20 14:54:42 +02003276 /* we'll initiate a new check */
3277 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003278
Christopher Faulet61cc8522020-04-20 14:54:42 +02003279 check->state |= CHK_ST_INPROGRESS;
3280 b_reset(&check->bi);
3281 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003282
Christopher Faulet61cc8522020-04-20 14:54:42 +02003283 task_set_affinity(t, tid_bit);
3284 cs = check->cs;
3285 conn = cs_conn(cs);
3286 if (!conn) {
3287 check->current_step = NULL;
3288 tcpcheck_main(check);
3289 goto out_unlock;
3290 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003291
Christopher Faulet61cc8522020-04-20 14:54:42 +02003292 conn->flags |= CO_FL_ERROR;
3293 chk_report_conn_err(check, 0, 0);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003294
Christopher Faulet61cc8522020-04-20 14:54:42 +02003295 /* here, we have seen a synchronous error, no fd was allocated */
3296 task_set_affinity(t, MAX_THREADS_MASK);
3297 if (cs) {
3298 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003299 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003300 /* We may have been scheduled to run, and the
3301 * I/O handler expects to have a cs, so remove
3302 * the tasklet
3303 */
3304 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3305 cs_destroy(cs);
3306 cs = check->cs = NULL;
3307 conn = NULL;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003308 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003309
Christopher Faulet61cc8522020-04-20 14:54:42 +02003310 check->state &= ~CHK_ST_INPROGRESS;
3311 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003312
Christopher Faulet61cc8522020-04-20 14:54:42 +02003313 /* we allow up to min(inter, timeout.connect) for a connection
3314 * to establish but only when timeout.check is set
3315 * as it may be to short for a full check otherwise
3316 */
3317 while (tick_is_expired(t->expire, now_ms)) {
3318 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003319
Christopher Faulet61cc8522020-04-20 14:54:42 +02003320 t_con = tick_add(t->expire, proxy->timeout.connect);
3321 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3322 if (proxy->timeout.check)
3323 t->expire = tick_first(t->expire, t_con);
3324 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003325 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003326 else {
3327 /* there was a test running.
3328 * First, let's check whether there was an uncaught error,
3329 * which can happen on connect timeout or error.
3330 */
3331 if (check->result == CHK_RES_UNKNOWN) {
3332 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3333 chk_report_conn_err(check, 0, expired);
3334 }
3335 else
3336 goto out_unlock; /* timeout not reached, wait again */
3337 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003338
Christopher Faulet61cc8522020-04-20 14:54:42 +02003339 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003340
Christopher Faulet61cc8522020-04-20 14:54:42 +02003341 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003342
Christopher Faulet61cc8522020-04-20 14:54:42 +02003343 if (conn && conn->xprt) {
3344 /* The check was aborted and the connection was not yet closed.
3345 * This can happen upon timeout, or when an external event such
3346 * as a failed response coupled with "observe layer7" caused the
3347 * server state to be suddenly changed.
3348 */
3349 conn_sock_drain(conn);
3350 cs_close(cs);
3351 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003352
Christopher Faulet61cc8522020-04-20 14:54:42 +02003353 if (cs) {
3354 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003355 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003356 /* We may have been scheduled to run, and the
3357 * I/O handler expects to have a cs, so remove
3358 * the tasklet
3359 */
3360 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3361 cs_destroy(cs);
3362 cs = check->cs = NULL;
3363 conn = NULL;
3364 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003365
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003366 if (check->sess != NULL) {
3367 vars_prune(&check->vars, check->sess, NULL);
3368 session_free(check->sess);
3369 check->sess = NULL;
3370 }
3371
Christopher Faulet61cc8522020-04-20 14:54:42 +02003372 if (check->server) {
3373 if (check->result == CHK_RES_FAILED) {
3374 /* a failure or timeout detected */
3375 check_notify_failure(check);
3376 }
3377 else if (check->result == CHK_RES_CONDPASS) {
3378 /* check is OK but asks for stopping mode */
3379 check_notify_stopping(check);
3380 }
3381 else if (check->result == CHK_RES_PASSED) {
3382 /* a success was detected */
3383 check_notify_success(check);
3384 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003385 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003386 task_set_affinity(t, MAX_THREADS_MASK);
3387 check->state &= ~CHK_ST_INPROGRESS;
3388
3389 if (check->server) {
3390 rv = 0;
3391 if (global.spread_checks > 0) {
3392 rv = srv_getinter(check) * global.spread_checks / 100;
3393 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3394 }
3395 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003396 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003397 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003398
Christopher Faulet61cc8522020-04-20 14:54:42 +02003399 reschedule:
3400 while (tick_is_expired(t->expire, now_ms))
3401 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3402 out_unlock:
3403 if (check->server)
3404 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3405 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003406}
3407
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003408
Christopher Faulet61cc8522020-04-20 14:54:42 +02003409/**************************************************************************/
3410/******************* Internals to parse tcp-check rules *******************/
3411/**************************************************************************/
3412struct action_kw_list tcp_check_keywords = {
3413 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3414};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003415
Christopher Faulet61cc8522020-04-20 14:54:42 +02003416/* Return the struct action_kw associated to a keyword */
3417static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003418{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003419 return action_lookup(&tcp_check_keywords.list, kw);
3420}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003421
Christopher Faulet61cc8522020-04-20 14:54:42 +02003422static void action_kw_tcp_check_build_list(struct buffer *chk)
3423{
3424 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003425}
3426
Christopher Faulet61cc8522020-04-20 14:54:42 +02003427/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3428 * returned on error.
3429 */
3430static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3431 struct list *rules, struct action_kw *kw,
3432 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003433{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003434 struct tcpcheck_rule *chk = NULL;
3435 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003436
Christopher Faulet61cc8522020-04-20 14:54:42 +02003437 actrule = calloc(1, sizeof(*actrule));
3438 if (!actrule) {
3439 memprintf(errmsg, "out of memory");
3440 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003441 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003442 actrule->kw = kw;
3443 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003444
Christopher Faulet61cc8522020-04-20 14:54:42 +02003445 cur_arg++;
3446 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3447 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3448 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003449 }
3450
Christopher Faulet61cc8522020-04-20 14:54:42 +02003451 chk = calloc(1, sizeof(*chk));
3452 if (!chk) {
3453 memprintf(errmsg, "out of memory");
3454 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003455 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003456 chk->action = TCPCHK_ACT_ACTION_KW;
3457 chk->action_kw.rule = actrule;
3458 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003459
3460 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003461 free(actrule);
3462 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003463}
3464
Christopher Faulet61cc8522020-04-20 14:54:42 +02003465/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3466 * returned on error.
3467 */
3468static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3469 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003470{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003471 struct tcpcheck_rule *chk = NULL;
3472 struct sockaddr_storage *sk = NULL;
3473 char *comment = NULL, *sni = NULL, *alpn = NULL;
3474 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003475 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003476 unsigned short conn_opts = 0;
3477 long port = 0;
3478 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003479
Christopher Faulet61cc8522020-04-20 14:54:42 +02003480 list_for_each_entry(chk, rules, list) {
3481 if (chk->action == TCPCHK_ACT_CONNECT)
3482 break;
3483 if (chk->action == TCPCHK_ACT_COMMENT ||
3484 chk->action == TCPCHK_ACT_ACTION_KW ||
3485 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3486 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003487
Christopher Faulet61cc8522020-04-20 14:54:42 +02003488 memprintf(errmsg, "first step MUST also be a 'connect', "
3489 "optionnaly preceded by a 'set-var', an 'unset-var' or a 'comment', "
3490 "when there is a 'connect' step in the tcp-check ruleset");
3491 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003492 }
3493
Christopher Faulet61cc8522020-04-20 14:54:42 +02003494 cur_arg++;
3495 while (*(args[cur_arg])) {
3496 if (strcmp(args[cur_arg], "default") == 0)
3497 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3498 else if (strcmp(args[cur_arg], "addr") == 0) {
3499 int port1, port2;
3500 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003501
Christopher Faulet61cc8522020-04-20 14:54:42 +02003502 if (!*(args[cur_arg+1])) {
3503 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3504 goto error;
3505 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003506
Christopher Faulet61cc8522020-04-20 14:54:42 +02003507 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3508 if (!sk) {
3509 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3510 goto error;
3511 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003512
Christopher Faulet61cc8522020-04-20 14:54:42 +02003513 proto = protocol_by_family(sk->ss_family);
3514 if (!proto || !proto->connect) {
3515 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3516 args[cur_arg]);
3517 goto error;
3518 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003519
Christopher Faulet61cc8522020-04-20 14:54:42 +02003520 if (port1 != port2) {
3521 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3522 args[cur_arg], args[cur_arg+1]);
3523 goto error;
3524 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003525
Christopher Faulet61cc8522020-04-20 14:54:42 +02003526 cur_arg++;
3527 }
3528 else if (strcmp(args[cur_arg], "port") == 0) {
3529 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003530
Christopher Faulet61cc8522020-04-20 14:54:42 +02003531 if (!*(args[cur_arg+1])) {
3532 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3533 goto error;
3534 }
3535 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003536
Christopher Faulet61cc8522020-04-20 14:54:42 +02003537 port = 0;
3538 release_sample_expr(port_expr);
3539 p = args[cur_arg]; end = p + strlen(p);
3540 port = read_uint(&p, end);
3541 if (p != end) {
3542 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003543
Christopher Faulet61cc8522020-04-20 14:54:42 +02003544 px->conf.args.ctx = ARGC_SRV;
3545 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3546 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003547
Christopher Faulet61cc8522020-04-20 14:54:42 +02003548 if (!port_expr) {
3549 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3550 goto error;
3551 }
3552 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3553 memprintf(errmsg, "error detected while parsing port expression : "
3554 " fetch method '%s' extracts information from '%s', "
3555 "none of which is available here.\n",
3556 args[cur_arg], sample_src_names(port_expr->fetch->use));
3557 goto error;
3558 }
3559 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3560 }
3561 else if (port > 65535 || port < 1) {
3562 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3563 args[cur_arg]);
3564 goto error;
3565 }
3566 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003567 else if (strcmp(args[cur_arg], "proto") == 0) {
3568 if (!*(args[cur_arg+1])) {
3569 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3570 goto error;
3571 }
3572 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3573 if (!mux_proto) {
3574 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3575 goto error;
3576 }
3577 cur_arg++;
3578 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003579 else if (strcmp(args[cur_arg], "comment") == 0) {
3580 if (!*(args[cur_arg+1])) {
3581 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3582 goto error;
3583 }
3584 cur_arg++;
3585 free(comment);
3586 comment = strdup(args[cur_arg]);
3587 if (!comment) {
3588 memprintf(errmsg, "out of memory");
3589 goto error;
3590 }
3591 }
3592 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3593 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3594 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3595 conn_opts |= TCPCHK_OPT_SOCKS4;
3596 else if (strcmp(args[cur_arg], "linger") == 0)
3597 conn_opts |= TCPCHK_OPT_LINGER;
3598#ifdef USE_OPENSSL
3599 else if (strcmp(args[cur_arg], "ssl") == 0) {
3600 px->options |= PR_O_TCPCHK_SSL;
3601 conn_opts |= TCPCHK_OPT_SSL;
3602 }
3603 else if (strcmp(args[cur_arg], "sni") == 0) {
3604 if (!*(args[cur_arg+1])) {
3605 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3606 goto error;
3607 }
3608 cur_arg++;
3609 free(sni);
3610 sni = strdup(args[cur_arg]);
3611 if (!sni) {
3612 memprintf(errmsg, "out of memory");
3613 goto error;
3614 }
3615 }
3616 else if (strcmp(args[cur_arg], "alpn") == 0) {
3617#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3618 free(alpn);
3619 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3620 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3621 goto error;
3622 }
3623 cur_arg++;
3624#else
3625 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003626 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003627#endif
3628 }
3629#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003630
Christopher Faulet61cc8522020-04-20 14:54:42 +02003631 else {
3632 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3633#ifdef USE_OPENSSL
3634 ", 'ssl', 'sni', 'alpn'"
3635#endif /* USE_OPENSSL */
3636 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3637 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003638 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003639 }
3640 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003641 }
3642
Christopher Faulet61cc8522020-04-20 14:54:42 +02003643 chk = calloc(1, sizeof(*chk));
3644 if (!chk) {
3645 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003646 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003647 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003648 chk->action = TCPCHK_ACT_CONNECT;
3649 chk->comment = comment;
3650 chk->connect.port = port;
3651 chk->connect.options = conn_opts;
3652 chk->connect.sni = sni;
3653 chk->connect.alpn = alpn;
3654 chk->connect.alpn_len= alpn_len;
3655 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003656 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003657 if (sk)
3658 chk->connect.addr = *sk;
3659 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003660
Christopher Faulet61cc8522020-04-20 14:54:42 +02003661 error:
3662 free(alpn);
3663 free(sni);
3664 free(comment);
3665 release_sample_expr(port_expr);
3666 return NULL;
3667}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003668
Christopher Faulet61cc8522020-04-20 14:54:42 +02003669/* Parses and creates a tcp-check send rule. NULL is returned on error */
3670static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3671 const char *file, int line, char **errmsg)
3672{
3673 struct tcpcheck_rule *chk = NULL;
3674 char *comment = NULL, *data = NULL;
3675 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003676
Christopher Faulet61cc8522020-04-20 14:54:42 +02003677 type = ((strcmp(args[cur_arg], "send-binary") == 0) ? TCPCHK_SEND_BINARY : TCPCHK_SEND_STRING);
3678 if (!*(args[cur_arg+1])) {
3679 memprintf(errmsg, "'%s' expects a %s as argument",
3680 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003681 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003682 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003683
Christopher Faulet61cc8522020-04-20 14:54:42 +02003684 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003685
Christopher Faulet61cc8522020-04-20 14:54:42 +02003686 cur_arg += 2;
3687 while (*(args[cur_arg])) {
3688 if (strcmp(args[cur_arg], "comment") == 0) {
3689 if (!*(args[cur_arg+1])) {
3690 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3691 goto error;
3692 }
3693 cur_arg++;
3694 free(comment);
3695 comment = strdup(args[cur_arg]);
3696 if (!comment) {
3697 memprintf(errmsg, "out of memory");
3698 goto error;
3699 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003700 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003701 else if (strcmp(args[cur_arg], "log-format") == 0) {
3702 if (type == TCPCHK_SEND_BINARY)
3703 type = TCPCHK_SEND_BINARY_LF;
3704 else if (type == TCPCHK_SEND_STRING)
3705 type = TCPCHK_SEND_STRING_LF;
3706 }
3707 else {
3708 memprintf(errmsg, "expects 'comment', 'log-format' but got '%s' as argument.",
3709 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003710 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003711 }
3712 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003713 }
3714
Christopher Faulet61cc8522020-04-20 14:54:42 +02003715 chk = calloc(1, sizeof(*chk));
3716 if (!chk) {
3717 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003718 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003719 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003720 chk->action = TCPCHK_ACT_SEND;
3721 chk->comment = comment;
3722 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003723
Christopher Faulet61cc8522020-04-20 14:54:42 +02003724 switch (chk->send.type) {
3725 case TCPCHK_SEND_STRING:
3726 chk->send.data = ist2(strdup(data), strlen(data));
3727 if (!isttest(chk->send.data)) {
3728 memprintf(errmsg, "out of memory");
3729 goto error;
3730 }
3731 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003732 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003733 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003734 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003735 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3736 goto error;
3737 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003738 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003739 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003740 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003741 case TCPCHK_SEND_STRING_LF:
3742 case TCPCHK_SEND_BINARY_LF:
3743 LIST_INIT(&chk->send.fmt);
3744 px->conf.args.ctx = ARGC_SRV;
3745 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3746 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3747 goto error;
3748 }
3749 break;
3750 case TCPCHK_SEND_HTTP:
3751 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003752 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003753 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003754
Christopher Faulet61cc8522020-04-20 14:54:42 +02003755 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003756
Christopher Faulet61cc8522020-04-20 14:54:42 +02003757 error:
3758 free(chk);
3759 free(comment);
3760 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003761}
3762
Christopher Faulet61cc8522020-04-20 14:54:42 +02003763/* Parses and creates a http-check send rule. NULL is returned on error */
3764static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3765 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003766{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003767 struct tcpcheck_rule *chk = NULL;
3768 struct tcpcheck_http_hdr *hdr = NULL;
3769 struct http_hdr hdrs[global.tune.max_http_hdr];
3770 char *meth = NULL, *uri = NULL, *vsn = NULL;
3771 char *body = NULL, *comment = NULL;
3772 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003773 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003774
3775 cur_arg++;
3776 while (*(args[cur_arg])) {
3777 if (strcmp(args[cur_arg], "meth") == 0) {
3778 if (!*(args[cur_arg+1])) {
3779 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3780 goto error;
3781 }
3782 cur_arg++;
3783 meth = args[cur_arg];
3784 }
3785 else if (strcmp(args[cur_arg], "uri") == 0) {
3786 if (!*(args[cur_arg+1])) {
3787 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3788 goto error;
3789 }
3790 cur_arg++;
3791 uri = args[cur_arg];
3792 // TODO: log-format uri
3793 }
Christopher Faulet907701b2020-04-28 09:37:00 +02003794 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003795 if (!*(args[cur_arg+1])) {
3796 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3797 goto error;
3798 }
3799 cur_arg++;
3800 vsn = args[cur_arg];
3801 }
3802 else if (strcmp(args[cur_arg], "hdr") == 0) {
3803 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
3804 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
3805 goto error;
3806 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003807
3808 if (strcasecmp(args[cur_arg+1], "host") == 0) {
3809 if (host_hdr >= 0) {
3810 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
3811 args[cur_arg+1], istptr(hdrs[host_hdr].v));
3812 goto error;
3813 }
3814 host_hdr = i;
3815 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02003816 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
3817 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
3818 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
3819 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003820
Christopher Faulet61cc8522020-04-20 14:54:42 +02003821 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
3822 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
3823 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02003824 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003825 cur_arg += 2;
3826 }
3827 else if (strcmp(args[cur_arg], "body") == 0) {
3828 if (!*(args[cur_arg+1])) {
3829 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3830 goto error;
3831 }
3832 cur_arg++;
3833 body = args[cur_arg];
3834 // TODO: log-format body
3835 }
3836 else if (strcmp(args[cur_arg], "comment") == 0) {
3837 if (!*(args[cur_arg+1])) {
3838 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3839 goto error;
3840 }
3841 cur_arg++;
3842 free(comment);
3843 comment = strdup(args[cur_arg]);
3844 if (!comment) {
3845 memprintf(errmsg, "out of memory");
3846 goto error;
3847 }
3848 }
3849 else {
Christopher Faulet907701b2020-04-28 09:37:00 +02003850 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'ver', 'hdr' and 'body' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003851 args[cur_arg]);
3852 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003853 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003854 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003855 }
3856
Christopher Faulet61cc8522020-04-20 14:54:42 +02003857 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003858
Christopher Faulet61cc8522020-04-20 14:54:42 +02003859 chk = calloc(1, sizeof(*chk));
3860 if (!chk) {
3861 memprintf(errmsg, "out of memory");
3862 goto error;
3863 }
3864 chk->action = TCPCHK_ACT_SEND;
3865 chk->comment = comment; comment = NULL;
3866 chk->send.type = TCPCHK_SEND_HTTP;
3867 chk->send.http.flags = flags;
3868 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003869
Christopher Faulet61cc8522020-04-20 14:54:42 +02003870 if (meth) {
3871 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
3872 chk->send.http.meth.str.area = strdup(meth);
3873 chk->send.http.meth.str.data = strlen(meth);
3874 if (!chk->send.http.meth.str.area) {
3875 memprintf(errmsg, "out of memory");
3876 goto error;
3877 }
3878 }
3879 if (uri) {
3880 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
3881 if (!isttest(chk->send.http.uri)) {
3882 memprintf(errmsg, "out of memory");
3883 goto error;
3884 }
3885 }
3886 if (vsn) {
3887 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
3888 if (!isttest(chk->send.http.vsn)) {
3889 memprintf(errmsg, "out of memory");
3890 goto error;
3891 }
3892 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02003893 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003894 hdr = calloc(1, sizeof(*hdr));
3895 if (!hdr) {
3896 memprintf(errmsg, "out of memory");
3897 goto error;
3898 }
3899 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02003900 hdr->name = istdup(hdrs[i].n);
3901 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003902 memprintf(errmsg, "out of memory");
3903 goto error;
3904 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003905
Christopher Fauletb61caf42020-04-21 10:57:42 +02003906 ist0(hdrs[i].v);
3907 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 +02003908 goto error;
3909 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
3910 hdr = NULL;
3911 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003912
Christopher Faulet61cc8522020-04-20 14:54:42 +02003913 if (body) {
3914 chk->send.http.body = ist2(strdup(body), strlen(body));
3915 if (!isttest(chk->send.http.body)) {
3916 memprintf(errmsg, "out of memory");
3917 goto error;
3918 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003919 }
3920
Christopher Faulet61cc8522020-04-20 14:54:42 +02003921 return chk;
3922
3923 error:
3924 free_tcpcheck_http_hdr(hdr);
3925 free_tcpcheck(chk, 0);
3926 free(comment);
3927 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003928}
3929
Christopher Faulet61cc8522020-04-20 14:54:42 +02003930/* Parses and creates a http-check comment rule. NULL is returned on error */
3931static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
3932 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003933{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003934 struct tcpcheck_rule *chk = NULL;
3935 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003936
Christopher Faulet61cc8522020-04-20 14:54:42 +02003937 if (!*(args[cur_arg+1])) {
3938 memprintf(errmsg, "expects a string as argument");
3939 goto error;
3940 }
3941 cur_arg++;
3942 comment = strdup(args[cur_arg]);
3943 if (!comment) {
3944 memprintf(errmsg, "out of memory");
3945 goto error;
3946 }
Willy Tarreau04276f32017-01-06 17:41:29 +01003947
Christopher Faulet61cc8522020-04-20 14:54:42 +02003948 chk = calloc(1, sizeof(*chk));
3949 if (!chk) {
3950 memprintf(errmsg, "out of memory");
3951 goto error;
3952 }
3953 chk->action = TCPCHK_ACT_COMMENT;
3954 chk->comment = comment;
3955 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003956
Christopher Faulet61cc8522020-04-20 14:54:42 +02003957 error:
3958 free(comment);
3959 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003960}
3961
Christopher Faulet61cc8522020-04-20 14:54:42 +02003962/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
3963 * on error. <proto> is set to the right protocol flags (covered by the
3964 * TCPCHK_RULES_PROTO_CHK mask).
3965 */
3966static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
3967 struct list *rules, unsigned int proto,
3968 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01003969{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003970 struct tcpcheck_rule *prev_check, *chk = NULL;
3971 struct sample_expr *status_expr = NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02003972 char *on_success_msg, *on_error_msg, *comment, *pattern;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003973 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02003974 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
3975 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
3976 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003977 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02003978 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01003979
Christopher Faulet8021a5f2020-04-24 13:53:12 +02003980 on_success_msg = on_error_msg = comment = pattern = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003981 if (!*(args[cur_arg+1])) {
3982 memprintf(errmsg, "expects at least a matching pattern as arguments");
3983 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003984 }
3985
Christopher Faulet61cc8522020-04-20 14:54:42 +02003986 cur_arg++;
3987 while (*(args[cur_arg])) {
3988 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02003989
Christopher Faulet61cc8522020-04-20 14:54:42 +02003990 rescan:
3991 if (strcmp(args[cur_arg], "min-recv") == 0) {
3992 if (in_pattern) {
3993 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
3994 goto error;
3995 }
3996 if (!*(args[cur_arg+1])) {
3997 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
3998 goto error;
3999 }
4000 /* Use an signed integer here because of chksize */
4001 cur_arg++;
4002 min_recv = atol(args[cur_arg]);
4003 if (min_recv < -1 || min_recv > INT_MAX) {
4004 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4005 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004006 }
4007 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004008 else if (*(args[cur_arg]) == '!') {
4009 in_pattern = 1;
4010 while (*(args[cur_arg]) == '!') {
4011 inverse = !inverse;
4012 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004013 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004014 if (!*(args[cur_arg]))
4015 cur_arg++;
4016 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004017 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004018 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4019 if (type != TCPCHK_EXPECT_UNDEF) {
4020 memprintf(errmsg, "only on pattern expected");
4021 goto error;
4022 }
4023 if (proto != TCPCHK_RULES_HTTP_CHK)
4024 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_REGEX);
4025 else
4026 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_REGEX_BODY);
Christopher Faulete5870d82020-04-15 11:32:03 +02004027
Christopher Faulet61cc8522020-04-20 14:54:42 +02004028 if (!*(args[cur_arg+1])) {
4029 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4030 goto error;
4031 }
4032 cur_arg++;
4033 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004034 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004035 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4036 if (proto == TCPCHK_RULES_HTTP_CHK)
4037 goto bad_http_kw;
4038 if (type != TCPCHK_EXPECT_UNDEF) {
4039 memprintf(errmsg, "only on pattern expected");
4040 goto error;
4041 }
4042 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_REGEX_BINARY);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004043
Christopher Faulet61cc8522020-04-20 14:54:42 +02004044 if (!*(args[cur_arg+1])) {
4045 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4046 goto error;
4047 }
4048 cur_arg++;
4049 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004050 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004051 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4052 if (proto != TCPCHK_RULES_HTTP_CHK)
4053 goto bad_tcp_kw;
4054 if (type != TCPCHK_EXPECT_UNDEF) {
4055 memprintf(errmsg, "only on pattern expected");
4056 goto error;
4057 }
4058 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_REGEX_STATUS);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004059
Christopher Faulet61cc8522020-04-20 14:54:42 +02004060 if (!*(args[cur_arg+1])) {
4061 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4062 goto error;
4063 }
4064 cur_arg++;
4065 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004066 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004067 else if (strcmp(args[cur_arg], "custom") == 0) {
4068 if (in_pattern) {
4069 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4070 goto error;
4071 }
4072 if (type != TCPCHK_EXPECT_UNDEF) {
4073 memprintf(errmsg, "only on pattern expected");
4074 goto error;
4075 }
4076 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004077 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004078 else if (strcmp(args[cur_arg], "comment") == 0) {
4079 if (in_pattern) {
4080 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4081 goto error;
4082 }
4083 if (!*(args[cur_arg+1])) {
4084 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4085 goto error;
4086 }
4087 cur_arg++;
4088 free(comment);
4089 comment = strdup(args[cur_arg]);
4090 if (!comment) {
4091 memprintf(errmsg, "out of memory");
4092 goto error;
4093 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004094 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004095 else if (strcmp(args[cur_arg], "on-success") == 0) {
4096 if (in_pattern) {
4097 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4098 goto error;
4099 }
4100 if (!*(args[cur_arg+1])) {
4101 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4102 goto error;
4103 }
4104 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004105 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004106 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004107 else if (strcmp(args[cur_arg], "on-error") == 0) {
4108 if (in_pattern) {
4109 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4110 goto error;
4111 }
4112 if (!*(args[cur_arg+1])) {
4113 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4114 goto error;
4115 }
4116 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004117 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004118 }
4119 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4120 if (in_pattern) {
4121 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4122 goto error;
4123 }
4124 if (!*(args[cur_arg+1])) {
4125 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4126 goto error;
4127 }
4128 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4129 ok_st = HCHK_STATUS_L7OKD;
4130 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4131 ok_st = HCHK_STATUS_L7OKCD;
4132 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4133 ok_st = HCHK_STATUS_L6OK;
4134 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4135 ok_st = HCHK_STATUS_L4OK;
4136 else {
4137 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4138 args[cur_arg], args[cur_arg+1]);
4139 goto error;
4140 }
4141 cur_arg++;
4142 }
4143 else if (strcmp(args[cur_arg], "error-status") == 0) {
4144 if (in_pattern) {
4145 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4146 goto error;
4147 }
4148 if (!*(args[cur_arg+1])) {
4149 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4150 goto error;
4151 }
4152 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4153 err_st = HCHK_STATUS_L7RSP;
4154 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4155 err_st = HCHK_STATUS_L7STS;
4156 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4157 err_st = HCHK_STATUS_L6RSP;
4158 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4159 err_st = HCHK_STATUS_L4CON;
4160 else {
4161 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4162 args[cur_arg], args[cur_arg+1]);
4163 goto error;
4164 }
4165 cur_arg++;
4166 }
4167 else if (strcmp(args[cur_arg], "status-code") == 0) {
4168 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004169
Christopher Faulet61cc8522020-04-20 14:54:42 +02004170 if (in_pattern) {
4171 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4172 goto error;
4173 }
4174 if (!*(args[cur_arg+1])) {
4175 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4176 goto error;
4177 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004178
Christopher Faulet61cc8522020-04-20 14:54:42 +02004179 cur_arg++;
4180 release_sample_expr(status_expr);
4181 px->conf.args.ctx = ARGC_SRV;
4182 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4183 file, line, errmsg, &px->conf.args, NULL);
4184 if (!status_expr) {
4185 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4186 goto error;
4187 }
4188 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4189 memprintf(errmsg, "error detected while parsing status-code expression : "
4190 " fetch method '%s' extracts information from '%s', "
4191 "none of which is available here.\n",
4192 args[cur_arg], sample_src_names(status_expr->fetch->use));
4193 goto error;
4194 }
4195 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4196 }
4197 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4198 if (in_pattern) {
4199 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4200 goto error;
4201 }
4202 if (!*(args[cur_arg+1])) {
4203 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4204 goto error;
4205 }
4206 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4207 tout_st = HCHK_STATUS_L7TOUT;
4208 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4209 tout_st = HCHK_STATUS_L6TOUT;
4210 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4211 tout_st = HCHK_STATUS_L4TOUT;
4212 else {
4213 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4214 args[cur_arg], args[cur_arg+1]);
4215 goto error;
4216 }
4217 cur_arg++;
4218 }
4219 else {
4220 if (proto == TCPCHK_RULES_HTTP_CHK) {
4221 bad_http_kw:
4222 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]status', '[!]rstatus'"
4223 " or comment but got '%s' as argument.", args[cur_arg]);
4224 }
4225 else {
4226 bad_tcp_kw:
4227 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]rbinary'"
4228 " or comment but got '%s' as argument.", args[cur_arg]);
4229 }
4230 goto error;
4231 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004232
Christopher Faulet61cc8522020-04-20 14:54:42 +02004233 cur_arg++;
4234 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004235
Christopher Faulet61cc8522020-04-20 14:54:42 +02004236 chk = calloc(1, sizeof(*chk));
4237 if (!chk) {
4238 memprintf(errmsg, "out of memory");
4239 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004240 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004241 chk->action = TCPCHK_ACT_EXPECT;
4242 LIST_INIT(&chk->expect.onerror_fmt);
4243 LIST_INIT(&chk->expect.onsuccess_fmt);
4244 chk->comment = comment; comment = NULL;
4245 chk->expect.type = type;
4246 chk->expect.min_recv = min_recv;
4247 chk->expect.flags |= (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004248 chk->expect.ok_status = ok_st;
4249 chk->expect.err_status = err_st;
4250 chk->expect.tout_status = tout_st;
4251 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004252
Christopher Faulet61cc8522020-04-20 14:54:42 +02004253 if (on_success_msg) {
4254 px->conf.args.ctx = ARGC_SRV;
4255 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4256 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4257 goto error;
4258 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004259 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004260 if (on_error_msg) {
4261 px->conf.args.ctx = ARGC_SRV;
4262 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4263 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4264 goto error;
4265 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004266 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004267
Christopher Faulet61cc8522020-04-20 14:54:42 +02004268 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004269 case TCPCHK_EXPECT_HTTP_STATUS: {
4270 const char *p = pattern;
4271 unsigned int c1,c2;
4272
4273 chk->expect.codes.codes = NULL;
4274 chk->expect.codes.num = 0;
4275 while (1) {
4276 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4277 if (*p == '-') {
4278 p++;
4279 c2 = read_uint(&p, pattern + strlen(pattern));
4280 }
4281 if (c1 > c2) {
4282 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4283 goto error;
4284 }
4285
4286 chk->expect.codes.num++;
4287 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4288 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4289 if (!chk->expect.codes.codes) {
4290 memprintf(errmsg, "out of memory");
4291 goto error;
4292 }
4293 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4294 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4295
4296 if (*p == '\0')
4297 break;
4298 if (*p != ',') {
4299 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4300 goto error;
4301 }
4302 p++;
4303 }
4304 break;
4305 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004306 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004307 case TCPCHK_EXPECT_HTTP_BODY:
4308 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004309 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004310 memprintf(errmsg, "out of memory");
4311 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004312 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004313 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004314 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004315 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004316
4317 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004318 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4319 goto error;
4320 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004321 chk->expect.data.len = len;
4322 break;
4323 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004324 case TCPCHK_EXPECT_REGEX:
4325 case TCPCHK_EXPECT_REGEX_BINARY:
4326 case TCPCHK_EXPECT_HTTP_REGEX_STATUS:
4327 case TCPCHK_EXPECT_HTTP_REGEX_BODY:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004328 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004329 if (!chk->expect.regex)
4330 goto error;
4331 break;
4332 case TCPCHK_EXPECT_CUSTOM:
4333 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4334 break;
4335 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004336 memprintf(errmsg, "pattern not found");
4337 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004338 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004339
Christopher Faulet61cc8522020-04-20 14:54:42 +02004340 /* All tcp-check expect points back to the first inverse expect rule in
4341 * a chain of one or more expect rule, potentially itself.
4342 */
4343 chk->expect.head = chk;
4344 list_for_each_entry_rev(prev_check, rules, list) {
4345 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4346 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4347 chk->expect.head = prev_check;
4348 continue;
4349 }
4350 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4351 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004352 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004353 return chk;
4354
4355 error:
4356 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004357 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004358 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004359 return NULL;
4360}
4361
Christopher Faulet61cc8522020-04-20 14:54:42 +02004362/* Overwrites fields of the old http send rule with those of the new one. When
4363 * replaced, old values are freed and replaced by the new ones. New values are
4364 * not copied but transferred. At the end <new> should be empty and can be
4365 * safely released. This function never fails.
4366 */
4367static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004368{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004369 struct logformat_node *lf, *lfb;
4370 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004371
Christopher Faulet404f9192020-04-09 23:13:54 +02004372
Christopher Faulet61cc8522020-04-20 14:54:42 +02004373 if (new->send.http.meth.str.area) {
4374 free(old->send.http.meth.str.area);
4375 old->send.http.meth.meth = new->send.http.meth.meth;
4376 old->send.http.meth.str.area = new->send.http.meth.str.area;
4377 old->send.http.meth.str.data = new->send.http.meth.str.data;
4378 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004379 }
4380
Christopher Faulet61cc8522020-04-20 14:54:42 +02004381 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4382 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004383 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004384 else
4385 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4386 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4387 old->send.http.uri = new->send.http.uri;
4388 new->send.http.uri = IST_NULL;
4389 }
4390 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4391 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004392 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004393 else
4394 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4395 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4396 LIST_INIT(&old->send.http.uri_fmt);
4397 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4398 LIST_DEL(&lf->list);
4399 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4400 }
4401 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004402
Christopher Faulet61cc8522020-04-20 14:54:42 +02004403 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004404 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004405 old->send.http.vsn = new->send.http.vsn;
4406 new->send.http.vsn = IST_NULL;
4407 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004408
Christopher Faulet61cc8522020-04-20 14:54:42 +02004409 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4410 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4411 LIST_DEL(&hdr->list);
4412 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004413 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004414
4415 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4416 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004417 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004418 else
4419 free_tcpcheck_fmt(&old->send.http.body_fmt);
4420 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4421 old->send.http.body = new->send.http.body;
4422 new->send.http.body = IST_NULL;
4423 }
4424 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4425 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004426 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004427 else
4428 free_tcpcheck_fmt(&old->send.http.body_fmt);
4429 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4430 LIST_INIT(&old->send.http.body_fmt);
4431 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4432 LIST_DEL(&lf->list);
4433 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4434 }
4435 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004436}
4437
Christopher Faulet61cc8522020-04-20 14:54:42 +02004438/* Internal function used to add an http-check rule in a list during the config
4439 * parsing step. Depending on its type, and the previously inserted rules, a
4440 * specific action may be performed or an error may be reported. This functions
4441 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4442 * message.
4443 */
4444static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004445{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004446 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004447
Christopher Faulet61cc8522020-04-20 14:54:42 +02004448 /* the implicit send rule coming from an "option httpchk" line must be
4449 * merged with the first explici http-check send rule, if
4450 * any. Depdending the declaration order some tests are required.
4451 *
4452 * Some tests is also required for other kinds of http-check rules to be
4453 * sure the ruleset remains valid.
4454 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004455
Christopher Faulet61cc8522020-04-20 14:54:42 +02004456 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4457 /* Tries to add an implcit http-check send rule from an "option httpchk" line.
4458 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4459 * following tests are performed :
4460 *
4461 * 1- If there is no such rule or if it is not a send rule, the implicit send
4462 * rule is pushed in front of the ruleset
4463 *
4464 * 2- If it is another implicit send rule, it is replaced with the new one.
4465 *
4466 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
4467 * both, overwritting the old send rule (the explicit one) with info of the
4468 * new send rule (the implicit one).
4469 */
4470 r = get_first_tcpcheck_rule(rules);
4471 if (r && r->action == TCPCHK_ACT_CONNECT)
4472 r = get_next_tcpcheck_rule(rules, r);
4473 if (!r || r->action != TCPCHK_ACT_SEND)
4474 LIST_ADD(rules->list, &chk->list);
4475 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4476 LIST_DEL(&r->list);
4477 free_tcpcheck(r, 0);
4478 LIST_ADD(rules->list, &chk->list);
4479 }
4480 else {
4481 tcpcheck_overwrite_send_http_rule(r, chk);
4482 free_tcpcheck(chk, 0);
4483 }
4484 }
4485 else {
4486 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4487 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4488 * with an existing implicit send rule, if any. At the end, if there is no error,
4489 * the rule is appended to the list.
4490 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004491
Christopher Faulet61cc8522020-04-20 14:54:42 +02004492 r = get_last_tcpcheck_rule(rules);
4493 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4494 /* no error */;
4495 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4496 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4497 chk->index+1);
4498 return 0;
4499 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004500 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004501 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4502 chk->index+1);
4503 return 0;
4504 }
4505 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4506 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4507 chk->index+1);
4508 return 0;
4509 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004510
Christopher Faulet61cc8522020-04-20 14:54:42 +02004511 if (chk->action == TCPCHK_ACT_SEND) {
4512 r = get_first_tcpcheck_rule(rules);
4513 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4514 tcpcheck_overwrite_send_http_rule(r, chk);
4515 free_tcpcheck(chk, 0);
4516 LIST_DEL(&r->list);
4517 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4518 chk = r;
4519 }
4520 }
4521 LIST_ADDQ(rules->list, &chk->list);
4522 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004523 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004524}
4525
Christopher Faulet61cc8522020-04-20 14:54:42 +02004526/**************************************************************************/
4527/************************** Init/deinit checks ****************************/
4528/**************************************************************************/
4529static const char *init_check(struct check *check, int type)
4530{
4531 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004532
Christopher Faulet61cc8522020-04-20 14:54:42 +02004533 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4534 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004535
Christopher Faulet61cc8522020-04-20 14:54:42 +02004536 check->bi.area = calloc(check->bi.size, sizeof(char));
4537 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004538
Christopher Faulet61cc8522020-04-20 14:54:42 +02004539 if (!check->bi.area || !check->bo.area)
4540 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004541
Christopher Faulet61cc8522020-04-20 14:54:42 +02004542 check->wait_list.tasklet = tasklet_new();
4543 if (!check->wait_list.tasklet)
4544 return "out of memory while allocating check tasklet";
4545 check->wait_list.events = 0;
4546 check->wait_list.tasklet->process = event_srv_chk_io;
4547 check->wait_list.tasklet->context = check;
4548 return NULL;
4549}
4550
4551void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004552{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004553 task_destroy(check->task);
4554 if (check->wait_list.tasklet)
4555 tasklet_free(check->wait_list.tasklet);
4556
4557 free(check->bi.area);
4558 free(check->bo.area);
4559 if (check->cs) {
4560 free(check->cs->conn);
4561 check->cs->conn = NULL;
4562 cs_free(check->cs);
4563 check->cs = NULL;
4564 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004565}
4566
Christopher Faulet61cc8522020-04-20 14:54:42 +02004567/* manages a server health-check. Returns the time the task accepts to wait, or
4568 * TIME_ETERNITY for infinity.
4569 */
4570static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004571{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004572 struct check *check = context;
4573
4574 if (check->type == PR_O2_EXT_CHK)
4575 return process_chk_proc(t, context, state);
4576 return process_chk_conn(t, context, state);
4577
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004578}
4579
Christopher Faulet61cc8522020-04-20 14:54:42 +02004580
4581static int start_check_task(struct check *check, int mininter,
4582 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004583{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004584 struct task *t;
4585 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004586
Christopher Faulet61cc8522020-04-20 14:54:42 +02004587 if (check->type == PR_O2_EXT_CHK)
4588 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004589
Christopher Faulet61cc8522020-04-20 14:54:42 +02004590 /* task for the check */
4591 if ((t = task_new(thread_mask)) == NULL) {
4592 ha_alert("Starting [%s:%s] check: out of memory.\n",
4593 check->server->proxy->id, check->server->id);
4594 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004595 }
4596
Christopher Faulet61cc8522020-04-20 14:54:42 +02004597 check->task = t;
4598 t->process = process_chk;
4599 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004600
Christopher Faulet61cc8522020-04-20 14:54:42 +02004601 if (mininter < srv_getinter(check))
4602 mininter = srv_getinter(check);
4603
4604 if (global.max_spread_checks && mininter > global.max_spread_checks)
4605 mininter = global.max_spread_checks;
4606
4607 /* check this every ms */
4608 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
4609 check->start = now;
4610 task_queue(t);
4611
4612 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004613}
4614
Christopher Faulet61cc8522020-04-20 14:54:42 +02004615/* updates the server's weight during a warmup stage. Once the final weight is
4616 * reached, the task automatically stops. Note that any server status change
4617 * must have updated s->last_change accordingly.
4618 */
4619static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004620{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004621 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004622
Christopher Faulet61cc8522020-04-20 14:54:42 +02004623 /* by default, plan on stopping the task */
4624 t->expire = TICK_ETERNITY;
4625 if ((s->next_admin & SRV_ADMF_MAINT) ||
4626 (s->next_state != SRV_ST_STARTING))
4627 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02004628
Christopher Faulet61cc8522020-04-20 14:54:42 +02004629 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004630
Christopher Faulet61cc8522020-04-20 14:54:42 +02004631 /* recalculate the weights and update the state */
4632 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02004633
Christopher Faulet61cc8522020-04-20 14:54:42 +02004634 /* probably that we can refill this server with a bit more connections */
4635 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02004636
Christopher Faulet61cc8522020-04-20 14:54:42 +02004637 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02004638
Christopher Faulet61cc8522020-04-20 14:54:42 +02004639 /* get back there in 1 second or 1/20th of the slowstart interval,
4640 * whichever is greater, resulting in small 5% steps.
4641 */
4642 if (s->next_state == SRV_ST_STARTING)
4643 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
4644 return t;
4645}
4646
4647/*
4648 * Start health-check.
4649 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
4650 */
4651static int start_checks()
4652{
4653
4654 struct proxy *px;
4655 struct server *s;
4656 struct task *t;
4657 int nbcheck=0, mininter=0, srvpos=0;
4658
4659 /* 0- init the dummy frontend used to create all checks sessions */
4660 init_new_proxy(&checks_fe);
4661 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
4662 checks_fe.mode = PR_MODE_TCP;
4663 checks_fe.maxconn = 0;
4664 checks_fe.conn_retries = CONN_RETRIES;
4665 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
4666 checks_fe.timeout.client = TICK_ETERNITY;
4667
4668 /* 1- count the checkers to run simultaneously.
4669 * We also determine the minimum interval among all of those which
4670 * have an interval larger than SRV_CHK_INTER_THRES. This interval
4671 * will be used to spread their start-up date. Those which have
4672 * a shorter interval will start independently and will not dictate
4673 * too short an interval for all others.
4674 */
4675 for (px = proxies_list; px; px = px->next) {
4676 for (s = px->srv; s; s = s->next) {
4677 if (s->slowstart) {
4678 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
4679 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
4680 return ERR_ALERT | ERR_FATAL;
4681 }
4682 /* We need a warmup task that will be called when the server
4683 * state switches from down to up.
4684 */
4685 s->warmup = t;
4686 t->process = server_warmup;
4687 t->context = s;
4688 /* server can be in this state only because of */
4689 if (s->next_state == SRV_ST_STARTING)
4690 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 +02004691 }
4692
Christopher Faulet61cc8522020-04-20 14:54:42 +02004693 if (s->check.state & CHK_ST_CONFIGURED) {
4694 nbcheck++;
4695 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
4696 (!mininter || mininter > srv_getinter(&s->check)))
4697 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02004698 }
4699
Christopher Faulet61cc8522020-04-20 14:54:42 +02004700 if (s->agent.state & CHK_ST_CONFIGURED) {
4701 nbcheck++;
4702 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
4703 (!mininter || mininter > srv_getinter(&s->agent)))
4704 mininter = srv_getinter(&s->agent);
4705 }
Christopher Faulet5c288742020-03-31 08:15:58 +02004706 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004707 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02004708
Christopher Faulet61cc8522020-04-20 14:54:42 +02004709 if (!nbcheck)
4710 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02004711
Christopher Faulet61cc8522020-04-20 14:54:42 +02004712 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02004713
Christopher Faulet61cc8522020-04-20 14:54:42 +02004714 /*
4715 * 2- start them as far as possible from each others. For this, we will
4716 * start them after their interval set to the min interval divided by
4717 * the number of servers, weighted by the server's position in the list.
4718 */
4719 for (px = proxies_list; px; px = px->next) {
4720 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
4721 if (init_pid_list()) {
4722 ha_alert("Starting [%s] check: out of memory.\n", px->id);
4723 return ERR_ALERT | ERR_FATAL;
4724 }
4725 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02004726
Christopher Faulet61cc8522020-04-20 14:54:42 +02004727 for (s = px->srv; s; s = s->next) {
4728 /* A task for the main check */
4729 if (s->check.state & CHK_ST_CONFIGURED) {
4730 if (s->check.type == PR_O2_EXT_CHK) {
4731 if (!prepare_external_check(&s->check))
4732 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02004733 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004734 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
4735 return ERR_ALERT | ERR_FATAL;
4736 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02004737 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004738
Christopher Faulet61cc8522020-04-20 14:54:42 +02004739 /* A task for a auxiliary agent check */
4740 if (s->agent.state & CHK_ST_CONFIGURED) {
4741 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
4742 return ERR_ALERT | ERR_FATAL;
4743 }
4744 srvpos++;
4745 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004746 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004747 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004748 return 0;
4749}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004750
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004751
Christopher Faulet61cc8522020-04-20 14:54:42 +02004752/*
4753 * Return value:
4754 * the port to be used for the health check
4755 * 0 in case no port could be found for the check
4756 */
4757static int srv_check_healthcheck_port(struct check *chk)
4758{
4759 int i = 0;
4760 struct server *srv = NULL;
4761
4762 srv = chk->server;
4763
4764 /* by default, we use the health check port ocnfigured */
4765 if (chk->port > 0)
4766 return chk->port;
4767
4768 /* try to get the port from check_core.addr if check.port not set */
4769 i = get_host_port(&chk->addr);
4770 if (i > 0)
4771 return i;
4772
4773 /* try to get the port from server address */
4774 /* prevent MAPPORTS from working at this point, since checks could
4775 * not be performed in such case (MAPPORTS impose a relative ports
4776 * based on live traffic)
4777 */
4778 if (srv->flags & SRV_F_MAPPORTS)
4779 return 0;
4780
4781 i = srv->svc_port; /* by default */
4782 if (i > 0)
4783 return i;
4784
4785 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004786}
4787
Christopher Faulet61cc8522020-04-20 14:54:42 +02004788/* Initializes an health-check attached to the server <srv>. Non-zero is returned
4789 * if an error occurred.
4790 */
4791static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004792{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004793 const char *err;
4794 struct tcpcheck_rule *r;
4795 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004796
Christopher Faulet61cc8522020-04-20 14:54:42 +02004797 if (!srv->do_check)
4798 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004799
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004800
Christopher Faulet61cc8522020-04-20 14:54:42 +02004801 /* If neither a port nor an addr was specified and no check transport
4802 * layer is forced, then the transport layer used by the checks is the
4803 * same as for the production traffic. Otherwise we use raw_sock by
4804 * default, unless one is specified.
4805 */
4806 if (!srv->check.port && !is_addr(&srv->check.addr)) {
4807 if (!srv->check.use_ssl && srv->use_ssl != -1) {
4808 srv->check.use_ssl = srv->use_ssl;
4809 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004810 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004811 else if (srv->check.use_ssl == 1)
4812 srv->check.xprt = xprt_get(XPRT_SSL);
4813 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004814 }
4815
Christopher Faulet12882cf2020-04-23 15:50:18 +02004816 /* Inherit the mux protocol from the server if not already defined for
4817 * the check
4818 */
4819 if (srv->mux_proto && !srv->check.mux_proto)
4820 srv->check.mux_proto = srv->mux_proto;
4821
Christopher Faulet61cc8522020-04-20 14:54:42 +02004822 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004823
Christopher Faulet61cc8522020-04-20 14:54:42 +02004824 /* We need at least a service port, a check port or the first tcp-check
4825 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
4826 */
4827 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
4828 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
4829 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004830
Christopher Faulet61cc8522020-04-20 14:54:42 +02004831 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
4832 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
4833 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4834 ret |= ERR_ALERT | ERR_ABORT;
4835 goto out;
4836 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004837
Christopher Faulet61cc8522020-04-20 14:54:42 +02004838 /* search the first action (connect / send / expect) in the list */
4839 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
4840 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
4841 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
4842 "nor tcp_check rule 'connect' with port information.\n",
4843 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4844 ret |= ERR_ALERT | ERR_ABORT;
4845 goto out;
4846 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004847
Christopher Faulet61cc8522020-04-20 14:54:42 +02004848 /* scan the tcp-check ruleset to ensure a port has been configured */
4849 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
4850 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
4851 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
4852 "and a tcp_check rule 'connect' with no port information.\n",
4853 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4854 ret |= ERR_ALERT | ERR_ABORT;
4855 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02004856 }
Christopher Faulete5870d82020-04-15 11:32:03 +02004857 }
4858
Christopher Faulet61cc8522020-04-20 14:54:42 +02004859 init:
4860 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
4861 struct tcpcheck_ruleset *rs = NULL;
4862 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
4863 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02004864
Christopher Faulet61cc8522020-04-20 14:54:42 +02004865 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
4866 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02004867
Christopher Faulet61cc8522020-04-20 14:54:42 +02004868 rs = find_tcpcheck_ruleset("*tcp-check");
4869 if (!rs) {
4870 rs = create_tcpcheck_ruleset("*tcp-check");
4871 if (rs == NULL) {
4872 ha_alert("config: %s '%s': out of memory.\n",
4873 proxy_type_str(srv->proxy), srv->proxy->id);
4874 ret |= ERR_ALERT | ERR_FATAL;
4875 goto out;
4876 }
Christopher Faulete5870d82020-04-15 11:32:03 +02004877 }
4878
Christopher Faulet61cc8522020-04-20 14:54:42 +02004879 free_tcpcheck_vars(&rules->preset_vars);
4880 rules->list = &rs->rules;
4881 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004882 }
4883
Christopher Faulet61cc8522020-04-20 14:54:42 +02004884 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
4885 if (err) {
4886 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
4887 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
4888 ret |= ERR_ALERT | ERR_ABORT;
4889 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02004890 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004891 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
4892 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004893
Christopher Faulet61cc8522020-04-20 14:54:42 +02004894 out:
4895 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02004896}
4897
Christopher Faulet61cc8522020-04-20 14:54:42 +02004898/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
4899 * if an error occurred.
4900 */
4901static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02004902{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004903 struct tcpcheck_rule *chk;
4904 const char *err;
4905 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004906
Christopher Faulet61cc8522020-04-20 14:54:42 +02004907 if (!srv->do_agent)
4908 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02004909
Christopher Faulet61cc8522020-04-20 14:54:42 +02004910 /* If there is no connect rule preceeding all send / expect rules, an
4911 * implicit one is inserted before all others.
4912 */
4913 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
4914 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
4915 chk = calloc(1, sizeof(*chk));
4916 if (!chk) {
4917 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
4918 " to agent-check for server '%s' (out of memory).\n",
4919 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4920 ret |= ERR_ALERT | ERR_FATAL;
4921 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02004922 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004923 chk->action = TCPCHK_ACT_CONNECT;
4924 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
4925 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02004926 }
4927
Christopher Faulete5870d82020-04-15 11:32:03 +02004928
Christopher Faulet61cc8522020-04-20 14:54:42 +02004929 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
4930 if (err) {
4931 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
4932 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
4933 ret |= ERR_ALERT | ERR_ABORT;
4934 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02004935 }
4936
Christopher Faulet61cc8522020-04-20 14:54:42 +02004937 if (!srv->agent.inter)
4938 srv->agent.inter = srv->check.inter;
4939
4940 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
4941 global.maxsock++;
4942
4943 out:
4944 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02004945}
4946
Christopher Faulet61cc8522020-04-20 14:54:42 +02004947/* Check tcp-check health-check configuration for the proxy <px>. */
4948static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02004949{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004950 struct tcpcheck_rule *chk, *back;
4951 char *comment = NULL, *errmsg = NULL;
4952 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
4953 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004954
Christopher Faulet61cc8522020-04-20 14:54:42 +02004955 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
4956 deinit_proxy_tcpcheck(px);
4957 goto out;
4958 }
4959
4960 free(px->check_command);
4961 free(px->check_path);
4962 px->check_command = px->check_path = NULL;
4963
4964 if (!px->tcpcheck_rules.list) {
4965 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
4966 ret |= ERR_ALERT | ERR_FATAL;
4967 goto out;
4968 }
4969
4970 /* HTTP ruleset only : */
4971 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
4972 struct tcpcheck_rule *next;
4973
4974 /* move remaining implicit send rule from "option httpchk" line to the right place.
4975 * If such rule exists, it must be the first one. In this case, the rule is moved
4976 * after the first connect rule, if any. Otherwise, nothing is done.
4977 */
4978 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
4979 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4980 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
4981 if (next && next->action == TCPCHK_ACT_CONNECT) {
4982 LIST_DEL(&chk->list);
4983 LIST_ADD(&next->list, &chk->list);
4984 chk->index = next->index;
4985 }
Christopher Faulete5870d82020-04-15 11:32:03 +02004986 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004987
4988 /* add implicit expect rule if the last one is a send. It is inherited from previous
4989 * versions where the http expect rule was optional. Now it is possible to chained
4990 * send/expect rules but the last expect may still be implicit.
4991 */
4992 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
4993 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004994 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02004995 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
4996 px->conf.file, px->conf.line, &errmsg);
4997 if (!next) {
4998 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
4999 "(%s).\n", px->id, errmsg);
5000 free(errmsg);
5001 ret |= ERR_ALERT | ERR_FATAL;
5002 goto out;
5003 }
5004 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5005 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005006 }
5007 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005008
5009 /* For all ruleset: */
5010
5011 /* If there is no connect rule preceeding all send / expect rules, an
5012 * implicit one is inserted before all others.
5013 */
5014 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5015 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5016 chk = calloc(1, sizeof(*chk));
5017 if (!chk) {
5018 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5019 "(out of memory).\n", px->id);
5020 ret |= ERR_ALERT | ERR_FATAL;
5021 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005022 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005023 chk->action = TCPCHK_ACT_CONNECT;
5024 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5025 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5026 }
5027
5028 /* Remove all comment rules. To do so, when a such rule is found, the
5029 * comment is assigned to the following rule(s).
5030 */
5031 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5032 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5033 free(comment);
5034 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005035 }
5036
Christopher Faulet61cc8522020-04-20 14:54:42 +02005037 prev_action = chk->action;
5038 switch (chk->action) {
5039 case TCPCHK_ACT_COMMENT:
5040 free(comment);
5041 comment = chk->comment;
5042 LIST_DEL(&chk->list);
5043 free(chk);
5044 break;
5045 case TCPCHK_ACT_CONNECT:
5046 if (!chk->comment && comment)
5047 chk->comment = strdup(comment);
5048 /* fall though */
5049 case TCPCHK_ACT_ACTION_KW:
5050 free(comment);
5051 comment = NULL;
5052 break;
5053 case TCPCHK_ACT_SEND:
5054 case TCPCHK_ACT_EXPECT:
5055 if (!chk->comment && comment)
5056 chk->comment = strdup(comment);
5057 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005058 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005059 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005060 free(comment);
5061 comment = NULL;
5062
5063 out:
5064 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005065}
5066
Christopher Faulet61cc8522020-04-20 14:54:42 +02005067void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005068{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005069 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5070 px->tcpcheck_rules.flags = 0;
5071 px->tcpcheck_rules.list = NULL;
5072}
Christopher Faulete5870d82020-04-15 11:32:03 +02005073
Christopher Faulet61cc8522020-04-20 14:54:42 +02005074static void deinit_srv_check(struct server *srv)
5075{
5076 if (srv->check.state & CHK_ST_CONFIGURED)
5077 free_check(&srv->check);
5078 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5079 srv->do_check = 0;
5080}
Christopher Faulete5870d82020-04-15 11:32:03 +02005081
Christopher Faulet61cc8522020-04-20 14:54:42 +02005082
5083static void deinit_srv_agent_check(struct server *srv)
5084{
5085 if (srv->agent.tcpcheck_rules) {
5086 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5087 free(srv->agent.tcpcheck_rules);
5088 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005089 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005090
Christopher Faulet61cc8522020-04-20 14:54:42 +02005091 if (srv->agent.state & CHK_ST_CONFIGURED)
5092 free_check(&srv->agent);
5093
5094 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5095 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005096}
5097
Christopher Faulet61cc8522020-04-20 14:54:42 +02005098static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005099{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005100 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005101 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005102 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005103
Christopher Fauletd7cee712020-04-21 13:45:00 +02005104 node = ebpt_first(&shared_tcpchecks);
5105 while (node) {
5106 next = ebpt_next(node);
5107 ebpt_delete(node);
5108 free(node->key);
5109 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005110 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5111 LIST_DEL(&r->list);
5112 free_tcpcheck(r, 0);
5113 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005114 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005115 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005116 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005117}
Christopher Faulete5870d82020-04-15 11:32:03 +02005118
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005119
Christopher Faulet61cc8522020-04-20 14:54:42 +02005120REGISTER_POST_SERVER_CHECK(init_srv_check);
5121REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5122REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5123REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005124
Christopher Faulet61cc8522020-04-20 14:54:42 +02005125REGISTER_SERVER_DEINIT(deinit_srv_check);
5126REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5127REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5128REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005129
Christopher Faulet61cc8522020-04-20 14:54:42 +02005130/**************************************************************************/
5131/****************************** Email alerts ******************************/
5132/* NOTE: It may be pertinent to use an applet to handle email alerts */
5133/* instead of a tcp-check ruleset */
5134/**************************************************************************/
5135void email_alert_free(struct email_alert *alert)
5136{
5137 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005138
Christopher Faulet61cc8522020-04-20 14:54:42 +02005139 if (!alert)
5140 return;
5141
5142 if (alert->rules.list) {
5143 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5144 LIST_DEL(&rule->list);
5145 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005146 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005147 free_tcpcheck_vars(&alert->rules.preset_vars);
5148 free(alert->rules.list);
5149 alert->rules.list = NULL;
5150 }
5151 pool_free(pool_head_email_alert, alert);
5152}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005153
Christopher Faulet61cc8522020-04-20 14:54:42 +02005154static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5155{
5156 struct check *check = context;
5157 struct email_alertq *q;
5158 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005159
Christopher Faulet61cc8522020-04-20 14:54:42 +02005160 q = container_of(check, typeof(*q), check);
5161
5162 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5163 while (1) {
5164 if (!(check->state & CHK_ST_ENABLED)) {
5165 if (LIST_ISEMPTY(&q->email_alerts)) {
5166 /* All alerts processed, queue the task */
5167 t->expire = TICK_ETERNITY;
5168 task_queue(t);
5169 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005170 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005171
5172 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5173 LIST_DEL(&alert->list);
5174 t->expire = now_ms;
5175 check->tcpcheck_rules = &alert->rules;
5176 check->status = HCHK_STATUS_INI;
5177 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005178 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005179
5180 process_chk(t, context, state);
5181 if (check->state & CHK_ST_INPROGRESS)
5182 break;
5183
5184 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5185 email_alert_free(alert);
5186 check->tcpcheck_rules = NULL;
5187 check->server = NULL;
5188 check->state &= ~CHK_ST_ENABLED;
5189 }
5190 end:
5191 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5192 return t;
5193}
5194
5195/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5196 *
5197 * The function returns 1 in success case, otherwise, it returns 0 and err is
5198 * filled.
5199 */
5200int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5201{
5202 struct mailer *mailer;
5203 struct email_alertq *queues;
5204 const char *err_str;
5205 int i = 0;
5206
5207 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5208 memprintf(err, "out of memory while allocating mailer alerts queues");
5209 goto fail_no_queue;
5210 }
5211
5212 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5213 struct email_alertq *q = &queues[i];
5214 struct check *check = &q->check;
5215 struct task *t;
5216
5217 LIST_INIT(&q->email_alerts);
5218 HA_SPIN_INIT(&q->lock);
5219 check->inter = mls->timeout.mail;
5220 check->rise = DEF_AGENT_RISETIME;
5221 check->proxy = p;
5222 check->fall = DEF_AGENT_FALLTIME;
5223 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5224 memprintf(err, "%s", err_str);
5225 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005226 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005227
5228 check->xprt = mailer->xprt;
5229 check->addr = mailer->addr;
5230 check->port = get_host_port(&mailer->addr);
5231
5232 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5233 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005234 goto error;
5235 }
5236
Christopher Faulet61cc8522020-04-20 14:54:42 +02005237 check->task = t;
5238 t->process = process_email_alert;
5239 t->context = check;
5240
5241 /* check this in one ms */
5242 t->expire = TICK_ETERNITY;
5243 check->start = now;
5244 task_queue(t);
5245 }
5246
5247 mls->users++;
5248 free(p->email_alert.mailers.name);
5249 p->email_alert.mailers.m = mls;
5250 p->email_alert.queues = queues;
5251 return 0;
5252
5253 error:
5254 for (i = 0; i < mls->count; i++) {
5255 struct email_alertq *q = &queues[i];
5256 struct check *check = &q->check;
5257
5258 free_check(check);
5259 }
5260 free(queues);
5261 fail_no_queue:
5262 return 1;
5263}
5264
5265static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5266{
5267 struct tcpcheck_rule *tcpcheck, *prev_check;
5268 struct tcpcheck_expect *expect;
5269
5270 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5271 return 0;
5272 memset(tcpcheck, 0, sizeof(*tcpcheck));
5273 tcpcheck->action = TCPCHK_ACT_EXPECT;
5274
5275 expect = &tcpcheck->expect;
5276 expect->type = TCPCHK_EXPECT_STRING;
5277 LIST_INIT(&expect->onerror_fmt);
5278 LIST_INIT(&expect->onsuccess_fmt);
5279 expect->ok_status = HCHK_STATUS_L7OKD;
5280 expect->err_status = HCHK_STATUS_L7RSP;
5281 expect->tout_status = HCHK_STATUS_L7TOUT;
5282 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005283 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005284 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5285 return 0;
5286 }
5287
5288 /* All tcp-check expect points back to the first inverse expect rule
5289 * in a chain of one or more expect rule, potentially itself.
5290 */
5291 tcpcheck->expect.head = tcpcheck;
5292 list_for_each_entry_rev(prev_check, rules->list, list) {
5293 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5294 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5295 tcpcheck->expect.head = prev_check;
5296 continue;
5297 }
5298 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5299 break;
5300 }
5301 LIST_ADDQ(rules->list, &tcpcheck->list);
5302 return 1;
5303}
5304
5305static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5306{
5307 struct tcpcheck_rule *tcpcheck;
5308 struct tcpcheck_send *send;
5309 const char *in;
5310 char *dst;
5311 int i;
5312
5313 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5314 return 0;
5315 memset(tcpcheck, 0, sizeof(*tcpcheck));
5316 tcpcheck->action = TCPCHK_ACT_SEND;
5317
5318 send = &tcpcheck->send;
5319 send->type = TCPCHK_SEND_STRING;
5320
5321 for (i = 0; strs[i]; i++)
5322 send->data.len += strlen(strs[i]);
5323
Christopher Fauletb61caf42020-04-21 10:57:42 +02005324 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005325 if (!isttest(send->data)) {
5326 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5327 return 0;
5328 }
5329
Christopher Fauletb61caf42020-04-21 10:57:42 +02005330 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005331 for (i = 0; strs[i]; i++)
5332 for (in = strs[i]; (*dst = *in++); dst++);
5333 *dst = 0;
5334
5335 LIST_ADDQ(rules->list, &tcpcheck->list);
5336 return 1;
5337}
5338
5339static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5340 struct email_alertq *q, const char *msg)
5341{
5342 struct email_alert *alert;
5343 struct tcpcheck_rule *tcpcheck;
5344 struct check *check = &q->check;
5345
5346 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5347 goto error;
5348 LIST_INIT(&alert->list);
5349 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5350 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5351 if (!alert->rules.list)
5352 goto error;
5353 LIST_INIT(alert->rules.list);
5354 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5355 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005356
Christopher Faulet61cc8522020-04-20 14:54:42 +02005357 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5358 goto error;
5359 memset(tcpcheck, 0, sizeof(*tcpcheck));
5360 tcpcheck->action = TCPCHK_ACT_CONNECT;
5361 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005362
Christopher Faulet61cc8522020-04-20 14:54:42 +02005363 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005364
Christopher Faulet61cc8522020-04-20 14:54:42 +02005365 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005366 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005367
5368 {
5369 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5370 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5371 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005372 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005373
Christopher Faulet61cc8522020-04-20 14:54:42 +02005374 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5375 goto error;
5376
5377 {
5378 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5379 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005380 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005381 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005382
5383 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5384 goto error;
5385
5386 {
5387 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5388 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005389 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005390 }
5391
Christopher Faulet61cc8522020-04-20 14:54:42 +02005392 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5393 goto error;
5394
5395 {
5396 const char * const strs[2] = { "DATA\r\n" };
5397 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005398 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005399 }
5400
5401 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5402 goto error;
5403
5404 {
5405 struct tm tm;
5406 char datestr[48];
5407 const char * const strs[18] = {
5408 "From: ", p->email_alert.from, "\r\n",
5409 "To: ", p->email_alert.to, "\r\n",
5410 "Date: ", datestr, "\r\n",
5411 "Subject: [HAproxy Alert] ", msg, "\r\n",
5412 "\r\n",
5413 msg, "\r\n",
5414 "\r\n",
5415 ".\r\n",
5416 NULL
5417 };
5418
5419 get_localtime(date.tv_sec, &tm);
5420
5421 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005422 goto error;
5423 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005424
5425 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005426 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005427 }
5428
5429 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005430 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005431
5432 {
5433 const char * const strs[2] = { "QUIT\r\n" };
5434 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5435 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005436 }
5437
Christopher Faulet61cc8522020-04-20 14:54:42 +02005438 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5439 goto error;
5440
5441 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5442 task_wakeup(check->task, TASK_WOKEN_MSG);
5443 LIST_ADDQ(&q->email_alerts, &alert->list);
5444 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5445 return 1;
5446
5447error:
5448 email_alert_free(alert);
5449 return 0;
5450}
5451
5452static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5453{
5454 int i;
5455 struct mailer *mailer;
5456
5457 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5458 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5459 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5460 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5461 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005462 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005463 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005464
Christopher Faulet61cc8522020-04-20 14:54:42 +02005465 return;
5466}
5467
5468/*
5469 * Send email alert if configured.
5470 */
5471void send_email_alert(struct server *s, int level, const char *format, ...)
5472{
5473 va_list argp;
5474 char buf[1024];
5475 int len;
5476 struct proxy *p = s->proxy;
5477
5478 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5479 return;
5480
5481 va_start(argp, format);
5482 len = vsnprintf(buf, sizeof(buf), format, argp);
5483 va_end(argp);
5484
5485 if (len < 0 || len >= sizeof(buf)) {
5486 ha_alert("Email alert [%s] could not format message\n", p->id);
5487 return;
5488 }
5489
5490 enqueue_email_alert(p, s, buf);
5491}
5492
5493/**************************************************************************/
5494/************************** Check sample fetches **************************/
5495/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005496
Christopher Faulet61cc8522020-04-20 14:54:42 +02005497static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005498 { /* END */ },
5499}};
5500
5501INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5502
5503
5504/**************************************************************************/
5505/************************ Check's parsing functions ***********************/
5506/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005507/* Parses the "tcp-check" proxy keyword */
5508static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5509 struct proxy *defpx, const char *file, int line,
5510 char **errmsg)
5511{
Christopher Faulet404f9192020-04-09 23:13:54 +02005512 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005513 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005514 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005515
5516 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5517 ret = 1;
5518
Christopher Faulet404f9192020-04-09 23:13:54 +02005519 /* Deduce the ruleset name from the proxy info */
5520 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5521 ((curpx == defpx) ? "defaults" : curpx->id),
5522 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005523
Christopher Faulet61cc8522020-04-20 14:54:42 +02005524 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005525 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005526 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005527 if (rs == NULL) {
5528 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005529 goto error;
5530 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005531 }
5532
Gaetan Rivet5301b012020-02-25 17:19:17 +01005533 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005534 if (!LIST_ISEMPTY(&rs->rules)) {
5535 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005536 index = chk->index + 1;
5537 }
5538
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005539 cur_arg = 1;
5540 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005541 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005542 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005543 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005544 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005545 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005546 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005547 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005548 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005549 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5550
5551 if (!kw) {
5552 action_kw_tcp_check_build_list(&trash);
5553 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5554 "%s%s. but got '%s'",
5555 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5556 goto error;
5557 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005558 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005559 }
5560
5561 if (!chk) {
5562 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5563 goto error;
5564 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005565 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005566
5567 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005568 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005569 LIST_ADDQ(&rs->rules, &chk->list);
5570
5571 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005572 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005573 /* Use this ruleset if the proxy already has tcp-check enabled */
5574 curpx->tcpcheck_rules.list = &rs->rules;
5575 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5576 }
5577 else {
5578 /* mark this ruleset as unused for now */
5579 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
5580 }
5581
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005582 return ret;
5583
5584 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02005585 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005586 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005587 return -1;
5588}
5589
Christopher Faulet51b129f2020-04-09 15:54:18 +02005590/* Parses the "http-check" proxy keyword */
5591static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
5592 struct proxy *defpx, const char *file, int line,
5593 char **errmsg)
5594{
Christopher Faulete5870d82020-04-15 11:32:03 +02005595 struct tcpcheck_ruleset *rs = NULL;
5596 struct tcpcheck_rule *chk = NULL;
5597 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02005598
5599 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5600 ret = 1;
5601
5602 cur_arg = 1;
5603 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
5604 /* enable a graceful server shutdown on an HTTP 404 response */
5605 curpx->options |= PR_O_DISABLE404;
5606 if (too_many_args(1, args, errmsg, NULL))
5607 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02005608 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02005609 }
5610 else if (strcmp(args[cur_arg], "send-state") == 0) {
5611 /* enable emission of the apparent state of a server in HTTP checks */
5612 curpx->options2 |= PR_O2_CHK_SNDST;
5613 if (too_many_args(1, args, errmsg, NULL))
5614 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02005615 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02005616 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02005617
Christopher Faulete5870d82020-04-15 11:32:03 +02005618 /* Deduce the ruleset name from the proxy info */
5619 chunk_printf(&trash, "*http-check-%s_%s-%d",
5620 ((curpx == defpx) ? "defaults" : curpx->id),
5621 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02005622
Christopher Faulet61cc8522020-04-20 14:54:42 +02005623 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02005624 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005625 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02005626 if (rs == NULL) {
5627 memprintf(errmsg, "out of memory.\n");
5628 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02005629 }
5630 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02005631
Christopher Faulete5870d82020-04-15 11:32:03 +02005632 index = 0;
5633 if (!LIST_ISEMPTY(&rs->rules)) {
5634 chk = LIST_PREV(&rs->rules, typeof(chk), list);
5635 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
5636 index = chk->index + 1;
5637 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02005638
Christopher Faulete5870d82020-04-15 11:32:03 +02005639 if (strcmp(args[cur_arg], "connect") == 0)
5640 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
5641 else if (strcmp(args[cur_arg], "send") == 0)
5642 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
5643 else if (strcmp(args[cur_arg], "expect") == 0)
5644 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
5645 file, line, errmsg);
5646 else if (strcmp(args[cur_arg], "comment") == 0)
5647 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
5648 else {
5649 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02005650
Christopher Faulete5870d82020-04-15 11:32:03 +02005651 if (!kw) {
5652 action_kw_tcp_check_build_list(&trash);
5653 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
5654 " 'send', 'expect'%s%s. but got '%s'",
5655 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5656 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02005657 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005658 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
5659 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02005660
Christopher Faulete5870d82020-04-15 11:32:03 +02005661 if (!chk) {
5662 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5663 goto error;
5664 }
5665 ret = (*errmsg != NULL); /* Handle warning */
5666
5667 chk->index = index;
5668 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
5669 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5670 /* Use this ruleset if the proxy already has http-check enabled */
5671 curpx->tcpcheck_rules.list = &rs->rules;
5672 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
5673 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
5674 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5675 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02005676 goto error;
5677 }
5678 }
5679 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02005680 /* mark this ruleset as unused for now */
5681 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
5682 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02005683 }
5684
Christopher Faulete5870d82020-04-15 11:32:03 +02005685 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02005686 return ret;
5687
5688 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02005689 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005690 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02005691 return -1;
5692}
5693
Christopher Faulete9111b62020-04-09 18:12:08 +02005694/* Parses the "external-check" proxy keyword */
5695static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
5696 struct proxy *defpx, const char *file, int line,
5697 char **errmsg)
5698{
5699 int cur_arg, ret = 0;
5700
5701 cur_arg = 1;
5702 if (!*(args[cur_arg])) {
5703 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
5704 goto error;
5705 }
5706
5707 if (strcmp(args[cur_arg], "command") == 0) {
5708 if (too_many_args(2, args, errmsg, NULL))
5709 goto error;
5710 if (!*(args[cur_arg+1])) {
5711 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
5712 goto error;
5713 }
5714 free(curpx->check_command);
5715 curpx->check_command = strdup(args[cur_arg+1]);
5716 }
5717 else if (strcmp(args[cur_arg], "path") == 0) {
5718 if (too_many_args(2, args, errmsg, NULL))
5719 goto error;
5720 if (!*(args[cur_arg+1])) {
5721 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
5722 goto error;
5723 }
5724 free(curpx->check_path);
5725 curpx->check_path = strdup(args[cur_arg+1]);
5726 }
5727 else {
5728 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
5729 args[0], args[1]);
5730 goto error;
5731 }
5732
5733 ret = (*errmsg != NULL); /* Handle warning */
5734 return ret;
5735
5736error:
5737 return -1;
5738}
Christopher Faulet33f05df2020-04-01 11:08:50 +02005739
Christopher Faulet430e4802020-04-09 15:28:16 +02005740/* Parses the "option tcp-check" proxy keyword */
5741int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5742 const char *file, int line)
5743{
Christopher Faulet404f9192020-04-09 23:13:54 +02005744 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02005745 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5746 int err_code = 0;
5747
5748 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5749 err_code |= ERR_WARN;
5750
5751 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
5752 goto out;
5753
Christopher Faulet404f9192020-04-09 23:13:54 +02005754 curpx->options2 &= ~PR_O2_CHK_ANY;
5755 curpx->options2 |= PR_O2_TCPCHK_CHK;
5756
Christopher Fauletd7e63962020-04-17 20:15:59 +02005757 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005758 /* If a tcp-check rulesset is already set, do nothing */
5759 if (rules->list)
5760 goto out;
5761
5762 /* If a tcp-check ruleset is waiting to be used for the current proxy,
5763 * get it.
5764 */
5765 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
5766 goto curpx_ruleset;
5767
5768 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
5769 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005770 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005771 if (rs)
5772 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02005773 }
5774
Christopher Faulet404f9192020-04-09 23:13:54 +02005775 curpx_ruleset:
5776 /* Deduce the ruleset name from the proxy info */
5777 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5778 ((curpx == defpx) ? "defaults" : curpx->id),
5779 curpx->conf.file, curpx->conf.line);
5780
Christopher Faulet61cc8522020-04-20 14:54:42 +02005781 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005782 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005783 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005784 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02005785 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5786 goto error;
5787 }
Christopher Faulet430e4802020-04-09 15:28:16 +02005788 }
5789
Christopher Faulet404f9192020-04-09 23:13:54 +02005790 ruleset_found:
5791 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02005792 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02005793 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02005794
5795 out:
5796 return err_code;
5797
5798 error:
5799 err_code |= ERR_ALERT | ERR_FATAL;
5800 goto out;
5801}
Christopher Faulet33f05df2020-04-01 11:08:50 +02005802
5803/* Parses the "option redis-check" proxy keyword */
5804int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5805 const char *file, int line)
5806{
5807 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
5808 static char *redis_res = "+PONG\r\n";
5809
5810 struct tcpcheck_ruleset *rs = NULL;
5811 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5812 struct tcpcheck_rule *chk;
5813 char *errmsg = NULL;
5814 int err_code = 0;
5815
5816 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5817 err_code |= ERR_WARN;
5818
5819 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
5820 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02005821
5822 curpx->options2 &= ~PR_O2_CHK_ANY;
5823 curpx->options2 |= PR_O2_TCPCHK_CHK;
5824
5825 free_tcpcheck_vars(&rules->preset_vars);
5826 rules->list = NULL;
5827 rules->flags = 0;
5828
Christopher Faulet61cc8522020-04-20 14:54:42 +02005829 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02005830 if (rs)
5831 goto ruleset_found;
5832
Christopher Faulet61cc8522020-04-20 14:54:42 +02005833 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02005834 if (rs == NULL) {
5835 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5836 goto error;
5837 }
5838
5839 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
5840 1, curpx, &rs->rules, file, line, &errmsg);
5841 if (!chk) {
5842 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5843 goto error;
5844 }
5845 chk->index = 0;
5846 LIST_ADDQ(&rs->rules, &chk->list);
5847
5848 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
5849 "error-status", "L7STS",
Christopher Faulet78f371e2020-04-30 09:38:08 +02005850 "on-error", "%[check.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02005851 "on-success", "Redis server is ok",
5852 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02005853 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02005854 if (!chk) {
5855 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5856 goto error;
5857 }
5858 chk->index = 1;
5859 LIST_ADDQ(&rs->rules, &chk->list);
5860
Christopher Fauletd7cee712020-04-21 13:45:00 +02005861 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet33f05df2020-04-01 11:08:50 +02005862
5863 ruleset_found:
5864 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02005865 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02005866
5867 out:
5868 free(errmsg);
5869 return err_code;
5870
5871 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02005872 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02005873 err_code |= ERR_ALERT | ERR_FATAL;
5874 goto out;
5875}
5876
Christopher Faulet811f78c2020-04-01 11:10:27 +02005877
5878/* Parses the "option ssl-hello-chk" proxy keyword */
5879int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5880 const char *file, int line)
5881{
5882 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
5883 * ssl-hello-chk option to ensure that the remote server speaks SSL.
5884 *
5885 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
5886 */
5887 static char sslv3_client_hello[] = {
5888 "16" /* ContentType : 0x16 = Hanshake */
5889 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
5890 "0079" /* ContentLength : 0x79 bytes after this one */
5891 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
5892 "000075" /* HandshakeLength : 0x75 bytes after this one */
5893 "0300" /* Hello Version : 0x0300 = v3 */
5894 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
5895 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
5896 "00" /* Session ID length : empty (no session ID) */
5897 "004E" /* Cipher Suite Length : 78 bytes after this one */
5898 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
5899 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
5900 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
5901 "000D" "000E" "000F" "0010" /* various bit lengths, */
5902 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
5903 "0015" "0016" "0017" "0018"
5904 "0019" "001A" "001B" "002F"
5905 "0030" "0031" "0032" "0033"
5906 "0034" "0035" "0036" "0037"
5907 "0038" "0039" "003A"
5908 "01" /* Compression Length : 0x01 = 1 byte for types */
5909 "00" /* Compression Type : 0x00 = NULL compression */
5910 };
5911
5912 struct tcpcheck_ruleset *rs = NULL;
5913 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5914 struct tcpcheck_rule *chk;
5915 char *errmsg = NULL;
5916 int err_code = 0;
5917
5918 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5919 err_code |= ERR_WARN;
5920
5921 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
5922 goto out;
5923
Christopher Faulet811f78c2020-04-01 11:10:27 +02005924 curpx->options2 &= ~PR_O2_CHK_ANY;
5925 curpx->options2 |= PR_O2_TCPCHK_CHK;
5926
5927 free_tcpcheck_vars(&rules->preset_vars);
5928 rules->list = NULL;
5929 rules->flags = 0;
5930
Christopher Faulet61cc8522020-04-20 14:54:42 +02005931 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02005932 if (rs)
5933 goto ruleset_found;
5934
Christopher Faulet61cc8522020-04-20 14:54:42 +02005935 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02005936 if (rs == NULL) {
5937 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5938 goto error;
5939 }
5940
5941 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", sslv3_client_hello, "log-format", ""},
5942 1, curpx, &rs->rules, file, line, &errmsg);
5943 if (!chk) {
5944 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5945 goto error;
5946 }
5947 chk->index = 0;
5948 LIST_ADDQ(&rs->rules, &chk->list);
5949
5950 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02005951 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02005952 "error-status", "L6RSP", "tout-status", "L6TOUT",
5953 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02005954 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02005955 if (!chk) {
5956 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5957 goto error;
5958 }
5959 chk->index = 1;
5960 LIST_ADDQ(&rs->rules, &chk->list);
5961
Christopher Fauletd7cee712020-04-21 13:45:00 +02005962 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet811f78c2020-04-01 11:10:27 +02005963
5964 ruleset_found:
5965 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02005966 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02005967
5968 out:
5969 free(errmsg);
5970 return err_code;
5971
5972 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02005973 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02005974 err_code |= ERR_ALERT | ERR_FATAL;
5975 goto out;
5976}
5977
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02005978/* Parses the "option smtpchk" proxy keyword */
5979int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5980 const char *file, int line)
5981{
5982 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
5983
5984 struct tcpcheck_ruleset *rs = NULL;
5985 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5986 struct tcpcheck_rule *chk;
5987 struct tcpcheck_var *var = NULL;
5988 char *cmd = NULL, *errmsg = NULL;
5989 int err_code = 0;
5990
5991 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5992 err_code |= ERR_WARN;
5993
5994 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
5995 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02005996
5997 curpx->options2 &= ~PR_O2_CHK_ANY;
5998 curpx->options2 |= PR_O2_TCPCHK_CHK;
5999
6000 free_tcpcheck_vars(&rules->preset_vars);
6001 rules->list = NULL;
6002 rules->flags = 0;
6003
6004 cur_arg += 2;
6005 if (*args[cur_arg] && *args[cur_arg+1] &&
6006 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6007 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6008 if (cmd)
6009 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6010 }
6011 else {
6012 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6013 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6014 cmd = strdup("HELO localhost");
6015 }
6016
Christopher Fauletb61caf42020-04-21 10:57:42 +02006017 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006018 if (cmd == NULL || var == NULL) {
6019 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6020 goto error;
6021 }
6022 var->data.type = SMP_T_STR;
6023 var->data.u.str.area = cmd;
6024 var->data.u.str.data = strlen(cmd);
6025 LIST_INIT(&var->list);
6026 LIST_ADDQ(&rules->preset_vars, &var->list);
6027 cmd = NULL;
6028 var = NULL;
6029
Christopher Faulet61cc8522020-04-20 14:54:42 +02006030 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006031 if (rs)
6032 goto ruleset_found;
6033
Christopher Faulet61cc8522020-04-20 14:54:42 +02006034 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006035 if (rs == NULL) {
6036 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6037 goto error;
6038 }
6039
6040 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6041 1, curpx, &rs->rules, file, line, &errmsg);
6042 if (!chk) {
6043 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6044 goto error;
6045 }
6046 chk->index = 0;
6047 LIST_ADDQ(&rs->rules, &chk->list);
6048
6049 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6050 "min-recv", "4",
6051 "error-status", "L7RSP",
Christopher Faulet78f371e2020-04-30 09:38:08 +02006052 "on-error", "%[check.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006053 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006054 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006055 if (!chk) {
6056 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6057 goto error;
6058 }
6059 chk->index = 1;
6060 LIST_ADDQ(&rs->rules, &chk->list);
6061
6062 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6063 "min-recv", "4",
6064 "error-status", "L7STS",
6065 "on-error", "%[check.payload(4,0),ltrim(' '),cut_crlf]",
6066 "status-code", "check.payload(0,3)",
6067 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006068 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006069 if (!chk) {
6070 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6071 goto error;
6072 }
6073 chk->index = 2;
6074 LIST_ADDQ(&rs->rules, &chk->list);
6075
6076 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", smtp_req, "log-format", ""},
6077 1, curpx, &rs->rules, file, line, &errmsg);
6078 if (!chk) {
6079 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6080 goto error;
6081 }
6082 chk->index = 3;
6083 LIST_ADDQ(&rs->rules, &chk->list);
6084
6085 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6086 "min-recv", "4",
6087 "error-status", "L7STS",
6088 "on-error", "%[check.payload(4,0),ltrim(' '),cut_crlf]",
6089 "on-success", "%[check.payload(4,0),ltrim(' '),cut_crlf]",
6090 "status-code", "check.payload(0,3)",
6091 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006092 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006093 if (!chk) {
6094 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6095 goto error;
6096 }
6097 chk->index = 4;
6098 LIST_ADDQ(&rs->rules, &chk->list);
6099
Christopher Fauletd7cee712020-04-21 13:45:00 +02006100 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006101
6102 ruleset_found:
6103 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006104 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006105
6106 out:
6107 free(errmsg);
6108 return err_code;
6109
6110 error:
6111 free(cmd);
6112 free(var);
6113 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006114 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006115 err_code |= ERR_ALERT | ERR_FATAL;
6116 goto out;
6117}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006118
Christopher Fauletce355072020-04-02 11:44:39 +02006119/* Parses the "option pgsql-check" proxy keyword */
6120int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6121 const char *file, int line)
6122{
6123 static char pgsql_req[] = {
6124 "%[var(check.plen),htonl,hex]" /* The packet length*/
6125 "00030000" /* the version 3.0 */
6126 "7573657200" /* "user" key */
6127 "%[var(check.username),hex]00" /* the username */
6128 "00"
6129 };
6130
6131 struct tcpcheck_ruleset *rs = NULL;
6132 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6133 struct tcpcheck_rule *chk;
6134 struct tcpcheck_var *var = NULL;
6135 char *user = NULL, *errmsg = NULL;
6136 size_t packetlen = 0;
6137 int err_code = 0;
6138
6139 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6140 err_code |= ERR_WARN;
6141
6142 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6143 goto out;
6144
Christopher Fauletce355072020-04-02 11:44:39 +02006145 curpx->options2 &= ~PR_O2_CHK_ANY;
6146 curpx->options2 |= PR_O2_TCPCHK_CHK;
6147
6148 free_tcpcheck_vars(&rules->preset_vars);
6149 rules->list = NULL;
6150 rules->flags = 0;
6151
6152 cur_arg += 2;
6153 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6154 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6155 file, line, args[0], args[1]);
6156 goto error;
6157 }
6158 if (strcmp(args[cur_arg], "user") == 0) {
6159 packetlen = 15 + strlen(args[cur_arg+1]);
6160 user = strdup(args[cur_arg+1]);
6161
Christopher Fauletb61caf42020-04-21 10:57:42 +02006162 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006163 if (user == NULL || var == NULL) {
6164 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6165 goto error;
6166 }
6167 var->data.type = SMP_T_STR;
6168 var->data.u.str.area = user;
6169 var->data.u.str.data = strlen(user);
6170 LIST_INIT(&var->list);
6171 LIST_ADDQ(&rules->preset_vars, &var->list);
6172 user = NULL;
6173 var = NULL;
6174
Christopher Fauletb61caf42020-04-21 10:57:42 +02006175 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006176 if (var == NULL) {
6177 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6178 goto error;
6179 }
6180 var->data.type = SMP_T_SINT;
6181 var->data.u.sint = packetlen;
6182 LIST_INIT(&var->list);
6183 LIST_ADDQ(&rules->preset_vars, &var->list);
6184 var = NULL;
6185 }
6186 else {
6187 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6188 file, line, args[0], args[1]);
6189 goto error;
6190 }
6191
Christopher Faulet61cc8522020-04-20 14:54:42 +02006192 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006193 if (rs)
6194 goto ruleset_found;
6195
Christopher Faulet61cc8522020-04-20 14:54:42 +02006196 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006197 if (rs == NULL) {
6198 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6199 goto error;
6200 }
6201
6202 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6203 1, curpx, &rs->rules, file, line, &errmsg);
6204 if (!chk) {
6205 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6206 goto error;
6207 }
6208 chk->index = 0;
6209 LIST_ADDQ(&rs->rules, &chk->list);
6210
6211 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", pgsql_req, "log-format", ""},
6212 1, curpx, &rs->rules, file, line, &errmsg);
6213 if (!chk) {
6214 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6215 goto error;
6216 }
6217 chk->index = 1;
6218 LIST_ADDQ(&rs->rules, &chk->list);
6219
6220 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6221 "min-recv", "5",
6222 "error-status", "L7RSP",
6223 "on-error", "%[check.payload(6,0)]",
6224 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006225 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006226 if (!chk) {
6227 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6228 goto error;
6229 }
6230 chk->index = 2;
6231 LIST_ADDQ(&rs->rules, &chk->list);
6232
Christopher Fauletb841c742020-04-27 18:29:49 +02006233 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 +02006234 "min-recv", "9",
6235 "error-status", "L7STS",
6236 "on-success", "PostgreSQL server is ok",
6237 "on-error", "PostgreSQL unknown error",
6238 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006239 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006240 if (!chk) {
6241 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6242 goto error;
6243 }
6244 chk->index = 3;
6245 LIST_ADDQ(&rs->rules, &chk->list);
6246
Christopher Fauletd7cee712020-04-21 13:45:00 +02006247 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletce355072020-04-02 11:44:39 +02006248
6249 ruleset_found:
6250 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006251 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006252
6253 out:
6254 free(errmsg);
6255 return err_code;
6256
6257 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006258 free(user);
6259 free(var);
6260 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006261 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006262 err_code |= ERR_ALERT | ERR_FATAL;
6263 goto out;
6264}
6265
6266
6267/* Parses the "option mysql-check" proxy keyword */
6268int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6269 const char *file, int line)
6270{
6271 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6272 * const char mysql40_client_auth_pkt[] = {
6273 * "\x0e\x00\x00" // packet length
6274 * "\x01" // packet number
6275 * "\x00\x00" // client capabilities
6276 * "\x00\x00\x01" // max packet
6277 * "haproxy\x00" // username (null terminated string)
6278 * "\x00" // filler (always 0x00)
6279 * "\x01\x00\x00" // packet length
6280 * "\x00" // packet number
6281 * "\x01" // COM_QUIT command
6282 * };
6283 */
6284 static char mysql40_rsname[] = "*mysql40-check";
6285 static char mysql40_req[] = {
6286 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6287 "0080" /* client capabilities */
6288 "000001" /* max packet */
6289 "%[var(check.username),hex]00" /* the username */
6290 "00" /* filler (always 0x00) */
6291 "010000" /* packet length*/
6292 "00" /* sequence ID */
6293 "01" /* COM_QUIT command */
6294 };
6295
6296 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6297 * const char mysql41_client_auth_pkt[] = {
6298 * "\x0e\x00\x00\" // packet length
6299 * "\x01" // packet number
6300 * "\x00\x00\x00\x00" // client capabilities
6301 * "\x00\x00\x00\x01" // max packet
6302 * "\x21" // character set (UTF-8)
6303 * char[23] // All zeroes
6304 * "haproxy\x00" // username (null terminated string)
6305 * "\x00" // filler (always 0x00)
6306 * "\x01\x00\x00" // packet length
6307 * "\x00" // packet number
6308 * "\x01" // COM_QUIT command
6309 * };
6310 */
6311 static char mysql41_rsname[] = "*mysql41-check";
6312 static char mysql41_req[] = {
6313 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6314 "00820000" /* client capabilities */
6315 "00800001" /* max packet */
6316 "21" /* character set (UTF-8) */
6317 "000000000000000000000000" /* 23 bytes, al zeroes */
6318 "0000000000000000000000"
6319 "%[var(check.username),hex]00" /* the username */
6320 "00" /* filler (always 0x00) */
6321 "010000" /* packet length*/
6322 "00" /* sequence ID */
6323 "01" /* COM_QUIT command */
6324 };
6325
6326 struct tcpcheck_ruleset *rs = NULL;
6327 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6328 struct tcpcheck_rule *chk;
6329 struct tcpcheck_var *var = NULL;
6330 char *mysql_rsname = "*mysql-check";
6331 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6332 int index = 0, err_code = 0;
6333
6334 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6335 err_code |= ERR_WARN;
6336
6337 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6338 goto out;
6339
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006340 curpx->options2 &= ~PR_O2_CHK_ANY;
6341 curpx->options2 |= PR_O2_TCPCHK_CHK;
6342
6343 free_tcpcheck_vars(&rules->preset_vars);
6344 rules->list = NULL;
6345 rules->flags = 0;
6346
6347 cur_arg += 2;
6348 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006349 int packetlen, userlen;
6350
6351 if (strcmp(args[cur_arg], "user") != 0) {
6352 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6353 file, line, args[0], args[1], args[cur_arg]);
6354 goto error;
6355 }
6356
6357 if (*(args[cur_arg+1]) == 0) {
6358 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6359 file, line, args[0], args[1], args[cur_arg]);
6360 goto error;
6361 }
6362
6363 hdr = calloc(4, sizeof(*hdr));
6364 user = strdup(args[cur_arg+1]);
6365 userlen = strlen(args[cur_arg+1]);
6366
6367 if (hdr == NULL || user == NULL) {
6368 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6369 goto error;
6370 }
6371
6372 if (*args[cur_arg+2]) {
6373 if (strcmp(args[cur_arg+2], "post-41") != 0) {
6374 ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41' (got '%s').\n",
6375 file, line, args[cur_arg], args[cur_arg+2]);
6376 goto error;
6377 }
6378 packetlen = userlen + 7 + 27;
6379 mysql_req = mysql41_req;
6380 mysql_rsname = mysql41_rsname;
6381 }
6382 else {
6383 packetlen = userlen + 7;
6384 mysql_req = mysql40_req;
6385 mysql_rsname = mysql40_rsname;
6386 }
6387
6388 hdr[0] = (unsigned char)(packetlen & 0xff);
6389 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6390 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6391 hdr[3] = 1;
6392
Christopher Fauletb61caf42020-04-21 10:57:42 +02006393 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006394 if (var == NULL) {
6395 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6396 goto error;
6397 }
6398 var->data.type = SMP_T_STR;
6399 var->data.u.str.area = hdr;
6400 var->data.u.str.data = 4;
6401 LIST_INIT(&var->list);
6402 LIST_ADDQ(&rules->preset_vars, &var->list);
6403 hdr = NULL;
6404 var = NULL;
6405
Christopher Fauletb61caf42020-04-21 10:57:42 +02006406 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006407 if (var == NULL) {
6408 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6409 goto error;
6410 }
6411 var->data.type = SMP_T_STR;
6412 var->data.u.str.area = user;
6413 var->data.u.str.data = strlen(user);
6414 LIST_INIT(&var->list);
6415 LIST_ADDQ(&rules->preset_vars, &var->list);
6416 user = NULL;
6417 var = NULL;
6418 }
6419
Christopher Faulet61cc8522020-04-20 14:54:42 +02006420 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006421 if (rs)
6422 goto ruleset_found;
6423
Christopher Faulet61cc8522020-04-20 14:54:42 +02006424 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006425 if (rs == NULL) {
6426 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6427 goto error;
6428 }
6429
6430 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6431 1, curpx, &rs->rules, file, line, &errmsg);
6432 if (!chk) {
6433 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6434 goto error;
6435 }
6436 chk->index = index++;
6437 LIST_ADDQ(&rs->rules, &chk->list);
6438
6439 if (mysql_req) {
6440 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", mysql_req, "log-format", ""},
6441 1, curpx, &rs->rules, file, line, &errmsg);
6442 if (!chk) {
6443 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6444 goto error;
6445 }
6446 chk->index = index++;
6447 LIST_ADDQ(&rs->rules, &chk->list);
6448 }
6449
6450 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006451 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006452 if (!chk) {
6453 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6454 goto error;
6455 }
6456 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6457 chk->index = index++;
6458 LIST_ADDQ(&rs->rules, &chk->list);
6459
6460 if (mysql_req) {
6461 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006462 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006463 if (!chk) {
6464 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6465 goto error;
6466 }
6467 chk->expect.custom = tcpcheck_mysql_expect_ok;
6468 chk->index = index++;
6469 LIST_ADDQ(&rs->rules, &chk->list);
6470 }
6471
Christopher Fauletd7cee712020-04-21 13:45:00 +02006472 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006473
6474 ruleset_found:
6475 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006476 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006477
6478 out:
6479 free(errmsg);
6480 return err_code;
6481
6482 error:
6483 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006484 free(user);
6485 free(var);
6486 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006487 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006488 err_code |= ERR_ALERT | ERR_FATAL;
6489 goto out;
6490}
6491
Christopher Faulet1997eca2020-04-03 23:13:50 +02006492int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6493 const char *file, int line)
6494{
6495 static char *ldap_req = "300C020101600702010304008000";
6496
6497 struct tcpcheck_ruleset *rs = NULL;
6498 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6499 struct tcpcheck_rule *chk;
6500 char *errmsg = NULL;
6501 int err_code = 0;
6502
6503 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6504 err_code |= ERR_WARN;
6505
6506 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6507 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006508
6509 curpx->options2 &= ~PR_O2_CHK_ANY;
6510 curpx->options2 |= PR_O2_TCPCHK_CHK;
6511
6512 free_tcpcheck_vars(&rules->preset_vars);
6513 rules->list = NULL;
6514 rules->flags = 0;
6515
Christopher Faulet61cc8522020-04-20 14:54:42 +02006516 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006517 if (rs)
6518 goto ruleset_found;
6519
Christopher Faulet61cc8522020-04-20 14:54:42 +02006520 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006521 if (rs == NULL) {
6522 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6523 goto error;
6524 }
6525
6526 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6527 1, curpx, &rs->rules, file, line, &errmsg);
6528 if (!chk) {
6529 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6530 goto error;
6531 }
6532 chk->index = 0;
6533 LIST_ADDQ(&rs->rules, &chk->list);
6534
6535 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6536 "min-recv", "14",
6537 "on-error", "Not LDAPv3 protocol",
6538 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006539 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006540 if (!chk) {
6541 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6542 goto error;
6543 }
6544 chk->index = 1;
6545 LIST_ADDQ(&rs->rules, &chk->list);
6546
6547 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006548 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006549 if (!chk) {
6550 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6551 goto error;
6552 }
6553 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6554 chk->index = 2;
6555 LIST_ADDQ(&rs->rules, &chk->list);
6556
Christopher Fauletd7cee712020-04-21 13:45:00 +02006557 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006558
6559 ruleset_found:
6560 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006561 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006562
6563 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006564 free(errmsg);
6565 return err_code;
6566
6567 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006568 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006569 err_code |= ERR_ALERT | ERR_FATAL;
6570 goto out;
6571}
6572
6573int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6574 const char *file, int line)
6575{
6576 struct tcpcheck_ruleset *rs = NULL;
6577 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6578 struct tcpcheck_rule *chk;
6579 char *spop_req = NULL;
6580 char *errmsg = NULL;
6581 int spop_len = 0, err_code = 0;
6582
6583 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6584 err_code |= ERR_WARN;
6585
6586 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6587 goto out;
6588
Christopher Faulet267b01b2020-04-04 10:27:09 +02006589 curpx->options2 &= ~PR_O2_CHK_ANY;
6590 curpx->options2 |= PR_O2_TCPCHK_CHK;
6591
6592 free_tcpcheck_vars(&rules->preset_vars);
6593 rules->list = NULL;
6594 rules->flags = 0;
6595
6596
Christopher Faulet61cc8522020-04-20 14:54:42 +02006597 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02006598 if (rs)
6599 goto ruleset_found;
6600
Christopher Faulet61cc8522020-04-20 14:54:42 +02006601 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02006602 if (rs == NULL) {
6603 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6604 goto error;
6605 }
6606
6607 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
6608 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6609 goto error;
6610 }
6611 chunk_reset(&trash);
6612 dump_binary(&trash, spop_req, spop_len);
6613 trash.area[trash.data] = '\0';
6614
6615 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
6616 1, curpx, &rs->rules, file, line, &errmsg);
6617 if (!chk) {
6618 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6619 goto error;
6620 }
6621 chk->index = 0;
6622 LIST_ADDQ(&rs->rules, &chk->list);
6623
6624 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006625 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006626 if (!chk) {
6627 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6628 goto error;
6629 }
6630 chk->expect.custom = tcpcheck_spop_expect_agenthello;
6631 chk->index = 1;
6632 LIST_ADDQ(&rs->rules, &chk->list);
6633
Christopher Fauletd7cee712020-04-21 13:45:00 +02006634 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006635
6636 ruleset_found:
6637 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006638 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02006639
6640 out:
6641 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006642 free(errmsg);
6643 return err_code;
6644
6645 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006646 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006647 err_code |= ERR_ALERT | ERR_FATAL;
6648 goto out;
6649}
Christopher Fauletce355072020-04-02 11:44:39 +02006650
Christopher Faulete5870d82020-04-15 11:32:03 +02006651
6652struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
6653{
6654 struct tcpcheck_rule *chk = NULL;
6655 struct tcpcheck_http_hdr *hdr = NULL;
6656 char *meth = NULL, *uri = NULL, *vsn = NULL;
6657 char *hdrs, *body;
6658
6659 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
6660 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
6661 if (hdrs == body)
6662 hdrs = NULL;
6663 if (hdrs) {
6664 *hdrs = '\0';
6665 hdrs +=2;
6666 }
6667 if (body) {
6668 *body = '\0';
6669 body += 4;
6670 }
6671 if (hdrs || body) {
6672 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
6673 " Please, consider to use 'http-check send' directive instead.");
6674 }
6675
6676 chk = calloc(1, sizeof(*chk));
6677 if (!chk) {
6678 memprintf(errmsg, "out of memory");
6679 goto error;
6680 }
6681 chk->action = TCPCHK_ACT_SEND;
6682 chk->send.type = TCPCHK_SEND_HTTP;
6683 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
6684 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
6685 LIST_INIT(&chk->send.http.hdrs);
6686
6687 /* Copy the method, uri and version */
6688 if (*args[cur_arg]) {
6689 if (!*args[cur_arg+1])
6690 uri = args[cur_arg];
6691 else
6692 meth = args[cur_arg];
6693 }
6694 if (*args[cur_arg+1])
6695 uri = args[cur_arg+1];
6696 if (*args[cur_arg+2])
6697 vsn = args[cur_arg+2];
6698
6699 if (meth) {
6700 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
6701 chk->send.http.meth.str.area = strdup(meth);
6702 chk->send.http.meth.str.data = strlen(meth);
6703 if (!chk->send.http.meth.str.area) {
6704 memprintf(errmsg, "out of memory");
6705 goto error;
6706 }
6707 }
6708 if (uri) {
6709 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02006710 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02006711 memprintf(errmsg, "out of memory");
6712 goto error;
6713 }
6714 }
6715 if (vsn) {
6716 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02006717 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02006718 memprintf(errmsg, "out of memory");
6719 goto error;
6720 }
6721 }
6722
6723 /* Copy the header */
6724 if (hdrs) {
6725 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
6726 struct h1m h1m;
6727 int i, ret;
6728
6729 /* Build and parse the request */
6730 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
6731
6732 h1m.flags = H1_MF_HDRS_ONLY;
6733 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
6734 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
6735 &h1m, NULL);
6736 if (ret <= 0) {
6737 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
6738 goto error;
6739 }
6740
Christopher Fauletb61caf42020-04-21 10:57:42 +02006741 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02006742 hdr = calloc(1, sizeof(*hdr));
6743 if (!hdr) {
6744 memprintf(errmsg, "out of memory");
6745 goto error;
6746 }
6747 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02006748 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02006749 if (!hdr->name.ptr) {
6750 memprintf(errmsg, "out of memory");
6751 goto error;
6752 }
6753
Christopher Fauletb61caf42020-04-21 10:57:42 +02006754 ist0(tmp_hdrs[i].v);
6755 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 +02006756 goto error;
6757 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
6758 }
6759 }
6760
6761 /* Copy the body */
6762 if (body) {
6763 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02006764 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02006765 memprintf(errmsg, "out of memory");
6766 goto error;
6767 }
6768 }
6769
6770 return chk;
6771
6772 error:
6773 free_tcpcheck_http_hdr(hdr);
6774 free_tcpcheck(chk, 0);
6775 return NULL;
6776}
6777
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006778int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6779 const char *file, int line)
6780{
Christopher Faulete5870d82020-04-15 11:32:03 +02006781 struct tcpcheck_ruleset *rs = NULL;
6782 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6783 struct tcpcheck_rule *chk;
6784 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006785 int err_code = 0;
6786
6787 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6788 err_code |= ERR_WARN;
6789
6790 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6791 goto out;
6792
Christopher Faulete5870d82020-04-15 11:32:03 +02006793 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
6794 if (!chk) {
6795 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
6796 goto error;
6797 }
6798 if (errmsg) {
6799 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
6800 err_code |= ERR_WARN;
6801 free(errmsg);
6802 errmsg = NULL;
6803 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006804
Christopher Faulete5870d82020-04-15 11:32:03 +02006805 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006806 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02006807 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006808
Christopher Faulete5870d82020-04-15 11:32:03 +02006809 free_tcpcheck_vars(&rules->preset_vars);
6810 rules->list = NULL;
6811 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006812
Christopher Faulete5870d82020-04-15 11:32:03 +02006813 /* Deduce the ruleset name from the proxy info */
6814 chunk_printf(&trash, "*http-check-%s_%s-%d",
6815 ((curpx == defpx) ? "defaults" : curpx->id),
6816 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006817
Christopher Faulet61cc8522020-04-20 14:54:42 +02006818 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006819 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006820 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006821 if (rs == NULL) {
6822 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6823 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006824 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006825 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006826
Christopher Faulete5870d82020-04-15 11:32:03 +02006827 rules->list = &rs->rules;
6828 rules->flags |= TCPCHK_RULES_HTTP_CHK;
6829 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
6830 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
6831 rules->list = NULL;
6832 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006833 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006834
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006835 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02006836 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006837 return err_code;
6838
6839 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006840 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02006841 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02006842 err_code |= ERR_ALERT | ERR_FATAL;
6843 goto out;
6844}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006845
Christopher Faulet6f557912020-04-09 15:58:50 +02006846int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6847 const char *file, int line)
6848{
6849 int err_code = 0;
6850
Christopher Faulet6f557912020-04-09 15:58:50 +02006851 curpx->options2 &= ~PR_O2_CHK_ANY;
6852 curpx->options2 |= PR_O2_EXT_CHK;
6853 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6854 goto out;
6855
6856 out:
6857 return err_code;
6858}
6859
Christopher Fauletce8111e2020-04-06 15:04:11 +02006860/* Parse the "addr" server keyword */
6861static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6862 char **errmsg)
6863{
6864 struct sockaddr_storage *sk;
6865 struct protocol *proto;
6866 int port1, port2, err_code = 0;
6867
6868
6869 if (!*args[*cur_arg+1]) {
6870 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
6871 goto error;
6872 }
6873
6874 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
6875 if (!sk) {
6876 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
6877 goto error;
6878 }
6879
6880 proto = protocol_by_family(sk->ss_family);
6881 if (!proto || !proto->connect) {
6882 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
6883 args[*cur_arg], args[*cur_arg+1]);
6884 goto error;
6885 }
6886
6887 if (port1 != port2) {
6888 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
6889 args[*cur_arg], args[*cur_arg+1]);
6890 goto error;
6891 }
6892
6893 srv->check.addr = srv->agent.addr = *sk;
6894 srv->flags |= SRV_F_CHECKADDR;
6895 srv->flags |= SRV_F_AGENTADDR;
6896
6897 out:
6898 return err_code;
6899
6900 error:
6901 err_code |= ERR_ALERT | ERR_FATAL;
6902 goto out;
6903}
6904
6905
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006906/* Parse the "agent-addr" server keyword */
6907static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6908 char **errmsg)
6909{
6910 int err_code = 0;
6911
6912 if (!*(args[*cur_arg+1])) {
6913 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
6914 goto error;
6915 }
6916 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
6917 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
6918 goto error;
6919 }
6920
6921 out:
6922 return err_code;
6923
6924 error:
6925 err_code |= ERR_ALERT | ERR_FATAL;
6926 goto out;
6927}
6928
6929/* Parse the "agent-check" server keyword */
6930static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6931 char **errmsg)
6932{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006933 struct tcpcheck_ruleset *rs = NULL;
6934 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
6935 struct tcpcheck_rule *chk;
6936 int err_code = 0;
6937
6938 if (srv->do_agent)
6939 goto out;
6940
6941 if (!rules) {
6942 rules = calloc(1, sizeof(*rules));
6943 if (!rules) {
6944 memprintf(errmsg, "out of memory.");
6945 goto error;
6946 }
6947 LIST_INIT(&rules->preset_vars);
6948 srv->agent.tcpcheck_rules = rules;
6949 }
6950 rules->list = NULL;
6951 rules->flags = 0;
6952
Christopher Faulet61cc8522020-04-20 14:54:42 +02006953 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006954 if (rs)
6955 goto ruleset_found;
6956
Christopher Faulet61cc8522020-04-20 14:54:42 +02006957 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006958 if (rs == NULL) {
6959 memprintf(errmsg, "out of memory.");
6960 goto error;
6961 }
6962
6963 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", "%[var(check.agent_string)]", "log-format", ""},
6964 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
6965 if (!chk) {
6966 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
6967 goto error;
6968 }
6969 chk->index = 0;
6970 LIST_ADDQ(&rs->rules, &chk->list);
6971
6972 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006973 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
6974 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006975 if (!chk) {
6976 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
6977 goto error;
6978 }
6979 chk->expect.custom = tcpcheck_agent_expect_reply;
6980 chk->index = 1;
6981 LIST_ADDQ(&rs->rules, &chk->list);
6982
Christopher Fauletd7cee712020-04-21 13:45:00 +02006983 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006984
6985 ruleset_found:
6986 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006987 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006988 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006989
6990 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006991 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006992
6993 error:
6994 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006995 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006996 err_code |= ERR_ALERT | ERR_FATAL;
6997 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006998}
6999
7000/* Parse the "agent-inter" server keyword */
7001static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7002 char **errmsg)
7003{
7004 const char *err = NULL;
7005 unsigned int delay;
7006 int err_code = 0;
7007
7008 if (!*(args[*cur_arg+1])) {
7009 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7010 goto error;
7011 }
7012
7013 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7014 if (err == PARSE_TIME_OVER) {
7015 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7016 args[*cur_arg+1], args[*cur_arg], srv->id);
7017 goto error;
7018 }
7019 else if (err == PARSE_TIME_UNDER) {
7020 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7021 args[*cur_arg+1], args[*cur_arg], srv->id);
7022 goto error;
7023 }
7024 else if (err) {
7025 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7026 *err, srv->id);
7027 goto error;
7028 }
7029 if (delay <= 0) {
7030 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7031 delay, args[*cur_arg], srv->id);
7032 goto error;
7033 }
7034 srv->agent.inter = delay;
7035
7036 out:
7037 return err_code;
7038
7039 error:
7040 err_code |= ERR_ALERT | ERR_FATAL;
7041 goto out;
7042}
7043
7044/* Parse the "agent-port" server keyword */
7045static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7046 char **errmsg)
7047{
7048 int err_code = 0;
7049
7050 if (!*(args[*cur_arg+1])) {
7051 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7052 goto error;
7053 }
7054
7055 global.maxsock++;
7056 srv->agent.port = atol(args[*cur_arg+1]);
7057
7058 out:
7059 return err_code;
7060
7061 error:
7062 err_code |= ERR_ALERT | ERR_FATAL;
7063 goto out;
7064}
7065
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007066int set_srv_agent_send(struct server *srv, const char *send)
7067{
7068 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7069 struct tcpcheck_var *var = NULL;
7070 char *str;
7071
7072 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007073 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007074 if (str == NULL || var == NULL)
7075 goto error;
7076
7077 free_tcpcheck_vars(&rules->preset_vars);
7078
7079 var->data.type = SMP_T_STR;
7080 var->data.u.str.area = str;
7081 var->data.u.str.data = strlen(str);
7082 LIST_INIT(&var->list);
7083 LIST_ADDQ(&rules->preset_vars, &var->list);
7084
7085 return 1;
7086
7087 error:
7088 free(str);
7089 free(var);
7090 return 0;
7091}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007092
7093/* Parse the "agent-send" server keyword */
7094static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7095 char **errmsg)
7096{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007097 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007098 int err_code = 0;
7099
7100 if (!*(args[*cur_arg+1])) {
7101 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7102 goto error;
7103 }
7104
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007105 if (!rules) {
7106 rules = calloc(1, sizeof(*rules));
7107 if (!rules) {
7108 memprintf(errmsg, "out of memory.");
7109 goto error;
7110 }
7111 LIST_INIT(&rules->preset_vars);
7112 srv->agent.tcpcheck_rules = rules;
7113 }
7114
7115 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007116 memprintf(errmsg, "out of memory.");
7117 goto error;
7118 }
7119
7120 out:
7121 return err_code;
7122
7123 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007124 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007125 err_code |= ERR_ALERT | ERR_FATAL;
7126 goto out;
7127}
7128
7129/* Parse the "no-agent-send" server keyword */
7130static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7131 char **errmsg)
7132{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007133 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007134 return 0;
7135}
7136
Christopher Fauletce8111e2020-04-06 15:04:11 +02007137/* Parse the "check" server keyword */
7138static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7139 char **errmsg)
7140{
7141 srv->do_check = 1;
7142 return 0;
7143}
7144
7145/* Parse the "check-send-proxy" server keyword */
7146static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7147 char **errmsg)
7148{
7149 srv->check.send_proxy = 1;
7150 return 0;
7151}
7152
7153/* Parse the "check-via-socks4" server keyword */
7154static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7155 char **errmsg)
7156{
7157 srv->check.via_socks4 = 1;
7158 return 0;
7159}
7160
7161/* Parse the "no-check" server keyword */
7162static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7163 char **errmsg)
7164{
7165 deinit_srv_check(srv);
7166 return 0;
7167}
7168
7169/* Parse the "no-check-send-proxy" server keyword */
7170static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7171 char **errmsg)
7172{
7173 srv->check.send_proxy = 0;
7174 return 0;
7175}
7176
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007177/* parse the "check-proto" server keyword */
7178static int srv_parse_check_proto(char **args, int *cur_arg,
7179 struct proxy *px, struct server *newsrv, char **err)
7180{
7181 int err_code = 0;
7182
7183 if (!*args[*cur_arg + 1]) {
7184 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7185 goto error;
7186 }
7187 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7188 if (!newsrv->check.mux_proto) {
7189 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7190 goto error;
7191 }
7192
7193 out:
7194 return err_code;
7195
7196 error:
7197 err_code |= ERR_ALERT | ERR_FATAL;
7198 goto out;
7199}
7200
7201
Christopher Fauletce8111e2020-04-06 15:04:11 +02007202/* Parse the "rise" server keyword */
7203static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7204 char **errmsg)
7205{
7206 int err_code = 0;
7207
7208 if (!*args[*cur_arg + 1]) {
7209 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7210 goto error;
7211 }
7212
7213 srv->check.rise = atol(args[*cur_arg+1]);
7214 if (srv->check.rise <= 0) {
7215 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7216 goto error;
7217 }
7218
7219 if (srv->check.health)
7220 srv->check.health = srv->check.rise;
7221
7222 out:
7223 return err_code;
7224
7225 error:
7226 deinit_srv_agent_check(srv);
7227 err_code |= ERR_ALERT | ERR_FATAL;
7228 goto out;
7229 return 0;
7230}
7231
7232/* Parse the "fall" server keyword */
7233static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7234 char **errmsg)
7235{
7236 int err_code = 0;
7237
7238 if (!*args[*cur_arg + 1]) {
7239 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7240 goto error;
7241 }
7242
7243 srv->check.fall = atol(args[*cur_arg+1]);
7244 if (srv->check.fall <= 0) {
7245 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7246 goto error;
7247 }
7248
7249 out:
7250 return err_code;
7251
7252 error:
7253 deinit_srv_agent_check(srv);
7254 err_code |= ERR_ALERT | ERR_FATAL;
7255 goto out;
7256 return 0;
7257}
7258
7259/* Parse the "inter" server keyword */
7260static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7261 char **errmsg)
7262{
7263 const char *err = NULL;
7264 unsigned int delay;
7265 int err_code = 0;
7266
7267 if (!*(args[*cur_arg+1])) {
7268 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7269 goto error;
7270 }
7271
7272 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7273 if (err == PARSE_TIME_OVER) {
7274 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7275 args[*cur_arg+1], args[*cur_arg], srv->id);
7276 goto error;
7277 }
7278 else if (err == PARSE_TIME_UNDER) {
7279 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7280 args[*cur_arg+1], args[*cur_arg], srv->id);
7281 goto error;
7282 }
7283 else if (err) {
7284 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7285 *err, srv->id);
7286 goto error;
7287 }
7288 if (delay <= 0) {
7289 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7290 delay, args[*cur_arg], srv->id);
7291 goto error;
7292 }
7293 srv->check.inter = delay;
7294
7295 out:
7296 return err_code;
7297
7298 error:
7299 err_code |= ERR_ALERT | ERR_FATAL;
7300 goto out;
7301}
7302
7303
7304/* Parse the "fastinter" server keyword */
7305static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7306 char **errmsg)
7307{
7308 const char *err = NULL;
7309 unsigned int delay;
7310 int err_code = 0;
7311
7312 if (!*(args[*cur_arg+1])) {
7313 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7314 goto error;
7315 }
7316
7317 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7318 if (err == PARSE_TIME_OVER) {
7319 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7320 args[*cur_arg+1], args[*cur_arg], srv->id);
7321 goto error;
7322 }
7323 else if (err == PARSE_TIME_UNDER) {
7324 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7325 args[*cur_arg+1], args[*cur_arg], srv->id);
7326 goto error;
7327 }
7328 else if (err) {
7329 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7330 *err, srv->id);
7331 goto error;
7332 }
7333 if (delay <= 0) {
7334 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7335 delay, args[*cur_arg], srv->id);
7336 goto error;
7337 }
7338 srv->check.fastinter = delay;
7339
7340 out:
7341 return err_code;
7342
7343 error:
7344 err_code |= ERR_ALERT | ERR_FATAL;
7345 goto out;
7346}
7347
7348
7349/* Parse the "downinter" server keyword */
7350static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7351 char **errmsg)
7352{
7353 const char *err = NULL;
7354 unsigned int delay;
7355 int err_code = 0;
7356
7357 if (!*(args[*cur_arg+1])) {
7358 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7359 goto error;
7360 }
7361
7362 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7363 if (err == PARSE_TIME_OVER) {
7364 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7365 args[*cur_arg+1], args[*cur_arg], srv->id);
7366 goto error;
7367 }
7368 else if (err == PARSE_TIME_UNDER) {
7369 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7370 args[*cur_arg+1], args[*cur_arg], srv->id);
7371 goto error;
7372 }
7373 else if (err) {
7374 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7375 *err, srv->id);
7376 goto error;
7377 }
7378 if (delay <= 0) {
7379 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7380 delay, args[*cur_arg], srv->id);
7381 goto error;
7382 }
7383 srv->check.downinter = delay;
7384
7385 out:
7386 return err_code;
7387
7388 error:
7389 err_code |= ERR_ALERT | ERR_FATAL;
7390 goto out;
7391}
7392
7393/* Parse the "port" server keyword */
7394static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7395 char **errmsg)
7396{
7397 int err_code = 0;
7398
7399 if (!*(args[*cur_arg+1])) {
7400 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7401 goto error;
7402 }
7403
7404 global.maxsock++;
7405 srv->check.port = atol(args[*cur_arg+1]);
7406 srv->flags |= SRV_F_CHECKPORT;
7407
7408 out:
7409 return err_code;
7410
7411 error:
7412 err_code |= ERR_ALERT | ERR_FATAL;
7413 goto out;
7414}
7415
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007416static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007417 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7418 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7419 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007420 { 0, NULL, NULL },
7421}};
7422
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007423static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007424 { "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 +02007425 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7426 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7427 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7428 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7429 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007430 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007431 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007432 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7433 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007434 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007435 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7436 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7437 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7438 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7439 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7440 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7441 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7442 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007443 { NULL, NULL, 0 },
7444}};
7445
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007446INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007447INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007448
Willy Tarreaubd741542010-03-16 18:46:54 +01007449/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007450 * Local variables:
7451 * c-indent-level: 8
7452 * c-basic-offset: 8
7453 * End:
7454 */