blob: 172cdd6082ab692eb9e2a5fd3997759d373fe98f [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Willy Tarreau9b39dc52014-07-08 00:54:10 +020018#include <signal.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090019#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020020#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020021#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020023#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020024#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010025#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040027#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090028#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020030#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031#include <arpa/inet.h>
32
Christopher Fauletfd6c2292020-03-25 18:20:15 +010033#include <common/cfgparse.h>
Willy Tarreauc7e42382012-08-24 19:22:53 +020034#include <common/chunk.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020035#include <common/compat.h>
36#include <common/config.h>
37#include <common/mini-clist.h>
Willy Tarreau83749182007-04-15 20:56:27 +020038#include <common/standard.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020039#include <common/time.h>
Christopher Fauletcfda8472017-10-20 15:40:23 +020040#include <common/hathreads.h>
Christopher Faulete5870d82020-04-15 11:32:03 +020041#include <common/http.h>
42#include <common/h1.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020043#include <common/htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020044
45#include <types/global.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020046#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010047#include <types/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020048
Gaetan Rivet707b52f2020-02-21 18:14:59 +010049#include <proto/action.h>
Christopher Fauletba3c68f2020-04-01 16:27:05 +020050#include <proto/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020051#include <proto/backend.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020052#include <proto/checks.h>
William Lallemand9ed62032016-11-21 17:49:11 +010053#include <proto/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020054#include <proto/fd.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020055#include <proto/http_htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020056#include <proto/log.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020057#include <proto/mux_pt.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020058#include <proto/queue.h>
Willy Tarreauc6f4ce82009-06-10 11:09:37 +020059#include <proto/port_range.h>
Willy Tarreaue8c66af2008-01-13 18:40:14 +010060#include <proto/proto_tcp.h>
Baptiste Assmann69e273f2013-12-11 00:52:19 +010061#include <proto/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010062#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020063#include <proto/server.h>
Willy Tarreau48d6bf22016-06-21 16:27:34 +020064#include <proto/signal.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010065#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020066#include <proto/task.h>
Gaetan Rivet13a50432020-02-21 18:13:44 +010067#include <proto/vars.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020068#include <proto/log.h>
69#include <proto/dns.h>
70#include <proto/proto_udp.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020071#include <proto/ssl_sock.h>
Christopher Fauletb7d30092020-03-30 15:19:03 +020072#include <proto/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020073
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020074static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010075
Christopher Faulet61cc8522020-04-20 14:54:42 +020076static int wake_srv_chk(struct conn_stream *cs);
77struct data_cb check_conn_cb = {
78 .wake = wake_srv_chk,
79 .name = "CHCK",
80};
Christopher Fauletd7e63962020-04-17 20:15:59 +020081
Christopher Fauletd7cee712020-04-21 13:45:00 +020082/* Global tree to share all tcp-checks */
83struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020084
85
Willy Tarreau8ceae722018-11-26 11:58:30 +010086DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
87DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020088
Gaetan Rivet05d692d2020-02-14 17:42:54 +010089/* Dummy frontend used to create all checks sessions. */
90static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020091
Christopher Faulet61cc8522020-04-20 14:54:42 +020092/**************************************************************************/
93/************************ Handle check results ****************************/
94/**************************************************************************/
95struct check_status {
96 short result; /* one of SRV_CHK_* */
97 char *info; /* human readable short info */
98 char *desc; /* long description */
99};
100
101struct analyze_status {
102 char *desc; /* description */
103 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
104};
105
Simon Horman63a4a822012-03-19 07:24:41 +0900106static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100107 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
108 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200109 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200110
Willy Tarreau23964182014-05-20 20:56:30 +0200111 /* Below we have finished checks */
112 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100114
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100115 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100117 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
118 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
119 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200120
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100121 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
122 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
123 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200124
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100125 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
126 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200127
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200128 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200129
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100130 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
131 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
132 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900133
134 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
135 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200136 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200137};
138
Simon Horman63a4a822012-03-19 07:24:41 +0900139static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100140 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
141
142 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
143 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
144
145 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
146 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
147 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
148 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
149
150 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
151 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
152 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
153};
154
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100155/* checks if <err> is a real error for errno or one that can be ignored, and
156 * return 0 for these ones or <err> for real ones.
157 */
158static inline int unclean_errno(int err)
159{
160 if (err == EAGAIN || err == EINPROGRESS ||
161 err == EISCONN || err == EALREADY)
162 return 0;
163 return err;
164}
165
Christopher Faulet61cc8522020-04-20 14:54:42 +0200166/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200167const char *get_check_status_description(short check_status) {
168
169 const char *desc;
170
171 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200172 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200173 else
174 desc = NULL;
175
176 if (desc && *desc)
177 return desc;
178 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200179 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200180}
181
Christopher Faulet61cc8522020-04-20 14:54:42 +0200182/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200183const char *get_check_status_info(short check_status) {
184
185 const char *info;
186
187 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200188 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200189 else
190 info = NULL;
191
192 if (info && *info)
193 return info;
194 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200195 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200196}
197
Christopher Faulet61cc8522020-04-20 14:54:42 +0200198/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100199const char *get_analyze_status(short analyze_status) {
200
201 const char *desc;
202
203 if (analyze_status < HANA_STATUS_SIZE)
204 desc = analyze_statuses[analyze_status].desc;
205 else
206 desc = NULL;
207
208 if (desc && *desc)
209 return desc;
210 else
211 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
212}
213
Christopher Faulet61cc8522020-04-20 14:54:42 +0200214/* Sets check->status, update check->duration and fill check->result with an
215 * adequate CHK_RES_* value. The new check->health is computed based on the
216 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200217 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200218 * Shows information in logs about failed health check if server is UP or
219 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200220 */
Simon Horman4a741432013-02-23 15:35:38 +0900221static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100222{
Simon Horman4a741432013-02-23 15:35:38 +0900223 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200224 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200225 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900226
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200227 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100228 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900229 check->desc[0] = '\0';
230 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200231 return;
232 }
233
Simon Horman4a741432013-02-23 15:35:38 +0900234 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200235 return;
236
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200237 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900238 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
239 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200240 } else
Simon Horman4a741432013-02-23 15:35:38 +0900241 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200242
Simon Horman4a741432013-02-23 15:35:38 +0900243 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200244 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900245 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200246
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100247 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900248 check->duration = -1;
249 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200250 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900251 check->duration = tv_ms_elapsed(&check->start, &now);
252 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200253 }
254
Willy Tarreau23964182014-05-20 20:56:30 +0200255 /* no change is expected if no state change occurred */
256 if (check->result == CHK_RES_NEUTRAL)
257 return;
258
Olivier Houchard0923fa42019-01-11 18:43:04 +0100259 /* If the check was really just sending a mail, it won't have an
260 * associated server, so we're done now.
261 */
262 if (!s)
263 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200265
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200266 switch (check->result) {
267 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200268 /* Failure to connect to the agent as a secondary check should not
269 * cause the server to be marked down.
270 */
271 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900272 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200273 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100274 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200275 report = 1;
276 check->health--;
277 if (check->health < check->rise)
278 check->health = 0;
279 }
280 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200281
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200282 case CHK_RES_PASSED:
283 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
284 if ((check->health < check->rise + check->fall - 1) &&
285 (check->result == CHK_RES_PASSED || check->health > 0)) {
286 report = 1;
287 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200288
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200289 if (check->health >= check->rise)
290 check->health = check->rise + check->fall - 1; /* OK now */
291 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200292
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200293 /* clear consecutive_errors if observing is enabled */
294 if (s->onerror)
295 s->consecutive_errors = 0;
296 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100297
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200298 default:
299 break;
300 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200301
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200302 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
303 (status != prev_status || report)) {
304 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200305 "%s check for %sserver %s/%s %s%s",
306 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200307 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100308 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100309 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200310 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311
Emeric Brun5a133512017-10-19 14:42:30 +0200312 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200313
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100314 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200315 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
316 (check->health >= check->rise) ? check->fall : check->rise,
317 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200318
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200319 ha_warning("%s.\n", trash.area);
320 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
321 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200322 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200323}
324
Willy Tarreau4eec5472014-05-20 22:32:27 +0200325/* Marks the check <check>'s server down if the current check is already failed
326 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200327 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200328static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200329{
Simon Horman4a741432013-02-23 15:35:38 +0900330 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900331
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200332 /* The agent secondary check should only cause a server to be marked
333 * as down if check->status is HCHK_STATUS_L7STS, which indicates
334 * that the agent returned "fail", "stopped" or "down".
335 * The implication here is that failure to connect to the agent
336 * as a secondary check should not cause the server to be marked
337 * down. */
338 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
339 return;
340
Willy Tarreau4eec5472014-05-20 22:32:27 +0200341 if (check->health > 0)
342 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100343
Willy Tarreau4eec5472014-05-20 22:32:27 +0200344 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200345 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200346}
347
Willy Tarreauaf549582014-05-16 17:37:50 +0200348/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200349 * it isn't in maintenance, it is not tracking a down server and other checks
350 * comply. The rule is simple : by default, a server is up, unless any of the
351 * following conditions is true :
352 * - health check failed (check->health < rise)
353 * - agent check failed (agent->health < rise)
354 * - the server tracks a down server (track && track->state == STOPPED)
355 * Note that if the server has a slowstart, it will switch to STARTING instead
356 * of RUNNING. Also, only the health checks support the nolb mode, so the
357 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200358 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200359static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200360{
Simon Horman4a741432013-02-23 15:35:38 +0900361 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100362
Emeric Brun52a91d32017-08-31 14:41:55 +0200363 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200364 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100365
Emeric Brun52a91d32017-08-31 14:41:55 +0200366 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200367 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368
Willy Tarreau3e048382014-05-21 10:30:54 +0200369 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
370 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100371
Willy Tarreau3e048382014-05-21 10:30:54 +0200372 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
373 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200374
Emeric Brun52a91d32017-08-31 14:41:55 +0200375 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200376 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100377
Emeric Brun5a133512017-10-19 14:42:30 +0200378 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100379}
380
Willy Tarreaudb58b792014-05-21 13:57:23 +0200381/* Marks the check <check> as valid and tries to set its server into stopping mode
382 * if it was running or starting, and provided it isn't in maintenance and other
383 * checks comply. The conditions for the server to be marked in stopping mode are
384 * the same as for it to be turned up. Also, only the health checks support the
385 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200386 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200387static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200388{
Simon Horman4a741432013-02-23 15:35:38 +0900389 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100390
Emeric Brun52a91d32017-08-31 14:41:55 +0200391 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200392 return;
393
Willy Tarreaudb58b792014-05-21 13:57:23 +0200394 if (check->state & CHK_ST_AGENT)
395 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396
Emeric Brun52a91d32017-08-31 14:41:55 +0200397 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200398 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100399
Willy Tarreaudb58b792014-05-21 13:57:23 +0200400 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
401 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100402
Willy Tarreaudb58b792014-05-21 13:57:23 +0200403 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
404 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100405
Willy Tarreaub26881a2017-12-23 11:16:49 +0100406 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100407}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200408
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100409/* note: use health_adjust() only, which first checks that the observe mode is
410 * enabled.
411 */
412void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100413{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100414 int failed;
415 int expire;
416
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100417 if (s->observe >= HANA_OBS_SIZE)
418 return;
419
Willy Tarreaubb956662013-01-24 00:37:39 +0100420 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100421 return;
422
423 switch (analyze_statuses[status].lr[s->observe - 1]) {
424 case 1:
425 failed = 1;
426 break;
427
428 case 2:
429 failed = 0;
430 break;
431
432 default:
433 return;
434 }
435
436 if (!failed) {
437 /* good: clear consecutive_errors */
438 s->consecutive_errors = 0;
439 return;
440 }
441
Olivier Houchard7059c552019-03-08 18:49:32 +0100442 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100443
444 if (s->consecutive_errors < s->consecutive_errors_limit)
445 return;
446
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100447 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
448 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100449
450 switch (s->onerror) {
451 case HANA_ONERR_FASTINTER:
452 /* force fastinter - nothing to do here as all modes force it */
453 break;
454
455 case HANA_ONERR_SUDDTH:
456 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900457 if (s->check.health > s->check.rise)
458 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100459
460 /* no break - fall through */
461
462 case HANA_ONERR_FAILCHK:
463 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200464 set_server_check_status(&s->check, HCHK_STATUS_HANA,
465 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200466 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100467 break;
468
469 case HANA_ONERR_MARKDWN:
470 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900471 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200472 set_server_check_status(&s->check, HCHK_STATUS_HANA,
473 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200474 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100475 break;
476
477 default:
478 /* write a warning? */
479 break;
480 }
481
482 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100483 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100484
Simon Horman66183002013-02-23 10:16:43 +0900485 if (s->check.fastinter) {
486 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300487 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200488 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300489 /* requeue check task with new expire */
490 task_queue(s->check.task);
491 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100492 }
Willy Tarreauef781042010-01-27 11:53:01 +0100493}
494
Christopher Faulet61cc8522020-04-20 14:54:42 +0200495/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100496 * closed, keep errno intact as it is supposed to contain the valid error code.
497 * If no error is reported, check the socket's error queue using getsockopt().
498 * Warning, this must be done only once when returning from poll, and never
499 * after an I/O error was attempted, otherwise the error queue might contain
500 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
501 * socket. Returns non-zero if an error was reported, zero if everything is
502 * clean (including a properly closed socket).
503 */
504static int retrieve_errno_from_socket(struct connection *conn)
505{
506 int skerr;
507 socklen_t lskerr = sizeof(skerr);
508
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100509 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100510 return 1;
511
Willy Tarreau3c728722014-01-23 13:50:42 +0100512 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100513 return 0;
514
Willy Tarreau585744b2017-08-24 14:31:19 +0200515 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100516 errno = skerr;
517
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100518 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100519
520 if (!errno) {
521 /* we could not retrieve an error, that does not mean there is
522 * none. Just don't change anything and only report the prior
523 * error if any.
524 */
525 if (conn->flags & CO_FL_ERROR)
526 return 1;
527 else
528 return 0;
529 }
530
531 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
532 return 1;
533}
534
Christopher Faulet61cc8522020-04-20 14:54:42 +0200535/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100536 * and adjust the server status accordingly. It may make use of <errno_bck>
537 * if non-null when the caller is absolutely certain of its validity (eg:
538 * checked just after a syscall). If the caller doesn't have a valid errno,
539 * it can pass zero, and retrieve_errno_from_socket() will be called to try
540 * to extract errno from the socket. If no error is reported, it will consider
541 * the <expired> flag. This is intended to be used when a connection error was
542 * reported in conn->flags or when a timeout was reported in <expired>. The
543 * function takes care of not updating a server status which was already set.
544 * All situations where at least one of <expired> or CO_FL_ERROR are set
545 * produce a status.
546 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200547static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200549 struct conn_stream *cs = check->cs;
550 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100551 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200552 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200553 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100555 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100556 return;
557
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100558 errno = unclean_errno(errno_bck);
559 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100560 retrieve_errno_from_socket(conn);
561
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200562 if (conn && !(conn->flags & CO_FL_ERROR) &&
563 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100564 return;
565
566 /* we'll try to build a meaningful error message depending on the
567 * context of the error possibly present in conn->err_code, and the
568 * socket error possibly collected above. This is useful to know the
569 * exact step of the L6 layer (eg: SSL handshake).
570 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200571 chk = get_trash_chunk();
572
Christopher Faulet799f3a42020-04-07 12:06:14 +0200573 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200574 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200575 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200576 if (!step)
577 chunk_printf(chk, " at initial connection step of tcp-check");
578 else {
579 chunk_printf(chk, " at step %d of tcp-check", step);
580 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200581 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
582 if (check->current_step->connect.port)
583 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200584 else
585 chunk_appendf(chk, " (connect)");
586 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200587 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
588 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100589
590 switch (expect->type) {
591 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200592 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100593 break;
594 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200595 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100596 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200597 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200598 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100599 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200600 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100601 chunk_appendf(chk, " (expect binary regex)");
602 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200603 case TCPCHK_EXPECT_STRING_LF:
604 chunk_appendf(chk, " (expect log-format string)");
605 break;
606 case TCPCHK_EXPECT_BINARY_LF:
607 chunk_appendf(chk, " (expect log-format binary)");
608 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200610 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200612 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200613 chunk_appendf(chk, " (expect HTTP status regex)");
614 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200615 case TCPCHK_EXPECT_HTTP_HEADER:
616 chunk_appendf(chk, " (expect HTTP header pattern)");
617 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200618 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200619 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200620 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200621 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200622 chunk_appendf(chk, " (expect HTTP body regex)");
623 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200624 case TCPCHK_EXPECT_HTTP_BODY_LF:
625 chunk_appendf(chk, " (expect log-format HTTP body)");
626 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200627 case TCPCHK_EXPECT_CUSTOM:
628 chunk_appendf(chk, " (expect custom function)");
629 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100630 case TCPCHK_EXPECT_UNDEF:
631 chunk_appendf(chk, " (undefined expect!)");
632 break;
633 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200634 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200635 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200636 chunk_appendf(chk, " (send)");
637 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200638
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200639 if (check->current_step && check->current_step->comment)
640 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200641 }
642 }
643
Willy Tarreau00149122017-10-04 18:05:01 +0200644 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100645 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200646 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
647 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100648 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
650 chk->area);
651 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100652 }
653 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100654 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200655 chunk_printf(&trash, "%s%s", strerror(errno),
656 chk->area);
657 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100658 }
659 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200660 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100661 }
662 }
663
Willy Tarreau00149122017-10-04 18:05:01 +0200664 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200665 /* NOTE: this is reported after <fall> tries */
666 chunk_printf(chk, "No port available for the TCP connection");
667 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
668 }
669
Willy Tarreau00149122017-10-04 18:05:01 +0200670 if (!conn) {
671 /* connection allocation error before the connection was established */
672 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
673 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100674 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100675 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200676 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100677 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
678 else if (expired)
679 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200680
681 /*
682 * might be due to a server IP change.
683 * Let's trigger a DNS resolution if none are currently running.
684 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100685 if (check->server)
686 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200687
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100688 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100689 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100690 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200691 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100692 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
693 else if (expired)
694 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
695 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200696 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 /* I/O error after connection was established and before we could diagnose */
698 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
699 }
700 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200701 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
702
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100703 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200704 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
705 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200706 tout = check->current_step->expect.tout_status;
707 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100708 }
709
710 return;
711}
712
Willy Tarreaubaaee002006-06-26 02:48:02 +0200713
Christopher Faulet61cc8522020-04-20 14:54:42 +0200714/**************************************************************************/
715/*************** Init/deinit tcp-check rules and ruleset ******************/
716/**************************************************************************/
717/* Releases memory allocated for a log-format string */
718static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200719{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200720 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100721
Christopher Faulet61cc8522020-04-20 14:54:42 +0200722 list_for_each_entry_safe(lf, lfb, fmt, list) {
723 LIST_DEL(&lf->list);
724 release_sample_expr(lf->expr);
725 free(lf->arg);
726 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100727 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200728}
729
Christopher Faulet61cc8522020-04-20 14:54:42 +0200730/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
731static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100732{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200733 if (!hdr)
734 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200735
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200737 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200738 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100739}
740
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741/* Releases memory allocated for an HTTP header list used in a tcp-check send
742 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200743 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200744static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200745{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200746 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200747
Christopher Faulet61cc8522020-04-20 14:54:42 +0200748 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
749 LIST_DEL(&hdr->list);
750 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200751 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200752}
753
Christopher Faulet61cc8522020-04-20 14:54:42 +0200754/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
755 * tcp-check was allocated using a memory pool (it is used to instantiate email
756 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200757 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200758static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200759{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200760 if (!rule)
761 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200762
Christopher Faulet61cc8522020-04-20 14:54:42 +0200763 free(rule->comment);
764 switch (rule->action) {
765 case TCPCHK_ACT_SEND:
766 switch (rule->send.type) {
767 case TCPCHK_SEND_STRING:
768 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200769 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200770 break;
771 case TCPCHK_SEND_STRING_LF:
772 case TCPCHK_SEND_BINARY_LF:
773 free_tcpcheck_fmt(&rule->send.fmt);
774 break;
775 case TCPCHK_SEND_HTTP:
776 free(rule->send.http.meth.str.area);
777 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200778 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200779 else
780 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200781 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200782 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
783 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200784 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200785 else
786 free_tcpcheck_fmt(&rule->send.http.body_fmt);
787 break;
788 case TCPCHK_SEND_UNDEF:
789 break;
790 }
791 break;
792 case TCPCHK_ACT_EXPECT:
793 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
794 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
795 release_sample_expr(rule->expect.status_expr);
796 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200797 case TCPCHK_EXPECT_HTTP_STATUS:
798 free(rule->expect.codes.codes);
799 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200800 case TCPCHK_EXPECT_STRING:
801 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200802 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200803 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200804 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200805 case TCPCHK_EXPECT_STRING_REGEX:
806 case TCPCHK_EXPECT_BINARY_REGEX:
807 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
808 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200809 regex_free(rule->expect.regex);
810 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200811 case TCPCHK_EXPECT_STRING_LF:
812 case TCPCHK_EXPECT_BINARY_LF:
813 case TCPCHK_EXPECT_HTTP_BODY_LF:
814 free_tcpcheck_fmt(&rule->expect.fmt);
815 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200816 case TCPCHK_EXPECT_HTTP_HEADER:
817 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
818 regex_free(rule->expect.hdr.name_re);
819 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
820 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
821 else
822 istfree(&rule->expect.hdr.name);
823
824 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
825 regex_free(rule->expect.hdr.value_re);
826 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
827 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
828 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
829 istfree(&rule->expect.hdr.value);
830 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200831 case TCPCHK_EXPECT_CUSTOM:
832 case TCPCHK_EXPECT_UNDEF:
833 break;
834 }
835 break;
836 case TCPCHK_ACT_CONNECT:
837 free(rule->connect.sni);
838 free(rule->connect.alpn);
839 release_sample_expr(rule->connect.port_expr);
840 break;
841 case TCPCHK_ACT_COMMENT:
842 break;
843 case TCPCHK_ACT_ACTION_KW:
844 free(rule->action_kw.rule);
845 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200846 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200847
848 if (in_pool)
849 pool_free(pool_head_tcpcheck_rule, rule);
850 else
851 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200852}
853
Christopher Faulet61cc8522020-04-20 14:54:42 +0200854/* Creates a tcp-check variable used in preset variables before executing a
855 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100856 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200857static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100858{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200859 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100860
Christopher Faulet61cc8522020-04-20 14:54:42 +0200861 var = calloc(1, sizeof(*var));
862 if (var == NULL)
863 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100864
Christopher Fauletb61caf42020-04-21 10:57:42 +0200865 var->name = istdup(name);
866 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200867 free(var);
868 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100869 }
Simon Horman98637e52014-06-20 12:30:16 +0900870
Christopher Faulet61cc8522020-04-20 14:54:42 +0200871 LIST_INIT(&var->list);
872 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900873}
874
Christopher Faulet61cc8522020-04-20 14:54:42 +0200875/* Releases memory allocated for a preset tcp-check variable */
876static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900877{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200878 if (!var)
879 return;
880
Christopher Fauletb61caf42020-04-21 10:57:42 +0200881 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200882 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
883 free(var->data.u.str.area);
884 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
885 free(var->data.u.meth.str.area);
886 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900887}
888
Christopher Faulet61cc8522020-04-20 14:54:42 +0200889/* Releases a list of preset tcp-check variables */
890static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900891{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200892 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200893
Christopher Faulet61cc8522020-04-20 14:54:42 +0200894 list_for_each_entry_safe(var, back, vars, list) {
895 LIST_DEL(&var->list);
896 free_tcpcheck_var(var);
897 }
Simon Horman98637e52014-06-20 12:30:16 +0900898}
899
Christopher Faulet61cc8522020-04-20 14:54:42 +0200900/* Duplicate a list of preset tcp-check variables */
901int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900902{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900904
Christopher Faulet61cc8522020-04-20 14:54:42 +0200905 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200906 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200907 if (!new)
908 goto error;
909 new->data.type = var->data.type;
910 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
911 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
912 goto error;
913 if (var->data.type == SMP_T_STR)
914 new->data.u.str.area[new->data.u.str.data] = 0;
915 }
916 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
917 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
918 goto error;
919 new->data.u.str.area[new->data.u.str.data] = 0;
920 new->data.u.meth.meth = var->data.u.meth.meth;
921 }
922 else
923 new->data.u = var->data.u;
924 LIST_ADDQ(dst, &new->list);
925 }
926 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900927
Christopher Faulet61cc8522020-04-20 14:54:42 +0200928 error:
929 free(new);
930 return 0;
931}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200932
Christopher Faulet61cc8522020-04-20 14:54:42 +0200933/* Looks for a shared tcp-check ruleset given its name. */
934static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
935{
936 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200937 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900938
Christopher Fauletd7cee712020-04-21 13:45:00 +0200939 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
940 if (node) {
941 rs = container_of(node, typeof(*rs), node);
942 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200943 }
944 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900945}
946
Christopher Faulet61cc8522020-04-20 14:54:42 +0200947/* Creates a new shared tcp-check ruleset */
948static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900949{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900951
Christopher Faulet61cc8522020-04-20 14:54:42 +0200952 rs = calloc(1, sizeof(*rs));
953 if (rs == NULL)
954 return NULL;
955
Christopher Fauletd7cee712020-04-21 13:45:00 +0200956 rs->node.key = strdup(name);
957 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200958 free(rs);
959 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900960 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200963 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900965}
966
Christopher Faulet61cc8522020-04-20 14:54:42 +0200967/* Releases memory allocated by a tcp-check ruleset. */
968static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900969{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200970 struct tcpcheck_rule *r, *rb;
971 if (!rs)
972 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200973
Christopher Fauletd7cee712020-04-21 13:45:00 +0200974 ebpt_delete(&rs->node);
975 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200976 list_for_each_entry_safe(r, rb, &rs->rules, list) {
977 LIST_DEL(&r->list);
978 free_tcpcheck(r, 0);
979 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200980 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900981}
982
Christopher Faulet61cc8522020-04-20 14:54:42 +0200983
984/**************************************************************************/
985/**************** Everything about tcp-checks execution *******************/
986/**************************************************************************/
987/* Returns the id of a step in a tcp-check ruleset */
988static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200989{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200990 if (!rule)
991 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900992
Christopher Faulet61cc8522020-04-20 14:54:42 +0200993 /* no last started step => first step */
994 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900995 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900996
Christopher Faulet61cc8522020-04-20 14:54:42 +0200997 /* last step is the first implicit connect */
998 if (rule->index == 0 &&
999 rule->action == TCPCHK_ACT_CONNECT &&
1000 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1001 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001002
Christopher Faulet61cc8522020-04-20 14:54:42 +02001003 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001004}
1005
Christopher Faulet61cc8522020-04-20 14:54:42 +02001006/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1007 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001008 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001010{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001011 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001012
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 list_for_each_entry(r, rules->list, list) {
1014 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1015 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001016 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001017 return NULL;
1018}
Cyril Bontéac92a062014-12-27 22:28:38 +01001019
Christopher Faulet61cc8522020-04-20 14:54:42 +02001020/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1021 * NULL if none was found.
1022 */
1023static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1024{
1025 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001026
Christopher Faulet61cc8522020-04-20 14:54:42 +02001027 list_for_each_entry_rev(r, rules->list, list) {
1028 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1029 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001030 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001031 return NULL;
1032}
Cyril Bontéac92a062014-12-27 22:28:38 +01001033
Christopher Faulet61cc8522020-04-20 14:54:42 +02001034/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1035 * <start> or NULL if non was found. If <start> is NULL, it relies on
1036 * get_first_tcpcheck_rule().
1037 */
1038static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1039{
1040 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001041
Christopher Faulet61cc8522020-04-20 14:54:42 +02001042 if (!start)
1043 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001044
Christopher Faulet61cc8522020-04-20 14:54:42 +02001045 r = LIST_NEXT(&start->list, typeof(r), list);
1046 list_for_each_entry_from(r, rules->list, list) {
1047 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1048 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001049 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001051}
Simon Horman98637e52014-06-20 12:30:16 +09001052
Simon Horman98637e52014-06-20 12:30:16 +09001053
Christopher Faulet61cc8522020-04-20 14:54:42 +02001054/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1055static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1056 int match, struct ist info)
1057{
1058 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001059
Christopher Faulet61cc8522020-04-20 14:54:42 +02001060 /* Follows these step to produce the info message:
1061 * 1. if info field is already provided, copy it
1062 * 2. if the expect rule provides an onerror log-format string,
1063 * use it to produce the message
1064 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1065 * 4. Otherwise produce the generic tcp-check info message
1066 */
1067 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001068 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001070 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001071 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1072 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1073 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001074 }
Simon Horman98637e52014-06-20 12:30:16 +09001075
Christopher Faulet61cc8522020-04-20 14:54:42 +02001076 if (check->type == PR_O2_TCPCHK_CHK &&
1077 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1078 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001079
Christopher Faulet61cc8522020-04-20 14:54:42 +02001080 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1081 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001082 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001083 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1084 break;
1085 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001087 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001088 tcpcheck_get_step_id(check, rule));
1089 break;
1090 case TCPCHK_EXPECT_BINARY:
1091 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1092 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001093 case TCPCHK_EXPECT_STRING_REGEX:
1094 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1095 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001096 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1097 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001098 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001100 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001101 case TCPCHK_EXPECT_STRING_LF:
1102 case TCPCHK_EXPECT_HTTP_BODY_LF:
1103 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1104 break;
1105 case TCPCHK_EXPECT_BINARY_LF:
1106 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1107 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001108 case TCPCHK_EXPECT_CUSTOM:
1109 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1110 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001111 case TCPCHK_EXPECT_HTTP_HEADER:
1112 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001113 case TCPCHK_EXPECT_UNDEF:
1114 /* Should never happen. */
1115 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001116 }
1117
Christopher Faulet61cc8522020-04-20 14:54:42 +02001118 comment:
1119 /* If the failing expect rule provides a comment, it is concatenated to
1120 * the info message.
1121 */
1122 if (rule->comment) {
1123 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001124 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001125 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001126
Christopher Faulet61cc8522020-04-20 14:54:42 +02001127 /* Finally, the check status code is set if the failing expect rule
1128 * defines a status expression.
1129 */
1130 if (rule->expect.status_expr) {
1131 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001132 rule->expect.status_expr, SMP_T_STR);
1133
1134 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1135 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001137 }
Simon Horman98637e52014-06-20 12:30:16 +09001138
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 *(b_tail(msg)) = '\0';
1140}
Cyril Bontéac92a062014-12-27 22:28:38 +01001141
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1143static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1144 struct ist info)
1145{
1146 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001147
Christopher Faulet61cc8522020-04-20 14:54:42 +02001148 /* Follows these step to produce the info message:
1149 * 1. if info field is already provided, copy it
1150 * 2. if the expect rule provides an onsucces log-format string,
1151 * use it to produce the message
1152 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1153 * 4. Otherwise produce the generic tcp-check info message
1154 */
1155 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001156 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001157 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1158 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1159 &rule->expect.onsuccess_fmt);
1160 else if (check->type == PR_O2_TCPCHK_CHK &&
1161 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1162 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001163
Christopher Faulet61cc8522020-04-20 14:54:42 +02001164 /* Finally, the check status code is set if the expect rule defines a
1165 * status expression.
1166 */
1167 if (rule->expect.status_expr) {
1168 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001169 rule->expect.status_expr, SMP_T_STR);
1170
1171 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1172 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001173 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001174 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001175
1176 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001177}
1178
Christopher Faulet61cc8522020-04-20 14:54:42 +02001179/* Builds the server state header used by HTTP health-checks */
1180static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001181{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001182 int sv_state;
1183 int ratio;
1184 char addr[46];
1185 char port[6];
1186 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1187 "UP %d/%d", "UP",
1188 "NOLB %d/%d", "NOLB",
1189 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001190
Christopher Faulet61cc8522020-04-20 14:54:42 +02001191 if (!(s->check.state & CHK_ST_ENABLED))
1192 sv_state = 6;
1193 else if (s->cur_state != SRV_ST_STOPPED) {
1194 if (s->check.health == s->check.rise + s->check.fall - 1)
1195 sv_state = 3; /* UP */
1196 else
1197 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001198
Christopher Faulet61cc8522020-04-20 14:54:42 +02001199 if (s->cur_state == SRV_ST_STOPPING)
1200 sv_state += 2;
1201 } else {
1202 if (s->check.health)
1203 sv_state = 1; /* going up */
1204 else
1205 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001206 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001207
Christopher Faulet61cc8522020-04-20 14:54:42 +02001208 chunk_appendf(buf, srv_hlt_st[sv_state],
1209 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1210 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001211
Christopher Faulet61cc8522020-04-20 14:54:42 +02001212 addr_to_str(&s->addr, addr, sizeof(addr));
1213 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1214 snprintf(port, sizeof(port), "%u", s->svc_port);
1215 else
1216 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001217
Christopher Faulet61cc8522020-04-20 14:54:42 +02001218 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1219 addr, port, s->proxy->id, s->id,
1220 global.node,
1221 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1222 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1223 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1224 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001225
Christopher Faulet61cc8522020-04-20 14:54:42 +02001226 if ((s->cur_state == SRV_ST_STARTING) &&
1227 now.tv_sec < s->last_change + s->slowstart &&
1228 now.tv_sec >= s->last_change) {
1229 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1230 chunk_appendf(buf, "; throttle=%d%%", ratio);
1231 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 return b_data(buf);
1234}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236/* Internal functions to parse and validate a MySQL packet in the context of an
1237 * expect rule. It start to parse the input buffer at the offset <offset>. If
1238 * <last_read> is set, no more data are expected.
1239 */
1240static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1241 unsigned int offset, int last_read)
1242{
1243 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1244 enum healthcheck_status status;
1245 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001246 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001247 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001248
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001249
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001251 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001252 if (!last_read)
1253 goto wait_more_data;
1254
1255 /* invalid length or truncated response */
1256 status = HCHK_STATUS_L7RSP;
1257 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001258 }
1259
Christopher Faulet61cc8522020-04-20 14:54:42 +02001260 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1261 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1262 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001263
Christopher Faulet61cc8522020-04-20 14:54:42 +02001264 if (b_data(&check->bi) < offset+plen+4) {
1265 if (!last_read)
1266 goto wait_more_data;
1267
1268 /* invalid length or truncated response */
1269 status = HCHK_STATUS_L7RSP;
1270 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001271 }
Simon Horman98637e52014-06-20 12:30:16 +09001272
Christopher Faulet61cc8522020-04-20 14:54:42 +02001273 if (*b_peek(&check->bi, offset+4) == '\xff') {
1274 /* MySQL Error packet always begin with field_count = 0xff */
1275 status = HCHK_STATUS_L7STS;
1276 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1277 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1278 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1279 goto error;
1280 }
Simon Horman98637e52014-06-20 12:30:16 +09001281
Christopher Faulet61cc8522020-04-20 14:54:42 +02001282 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1283 /* Not the last rule, continue */
1284 goto out;
1285 }
Simon Horman98637e52014-06-20 12:30:16 +09001286
Christopher Faulet61cc8522020-04-20 14:54:42 +02001287 /* We set the MySQL Version in description for information purpose
1288 * FIXME : it can be cool to use MySQL Version for other purpose,
1289 * like mark as down old MySQL server.
1290 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001291 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1292 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001293
Christopher Faulet61cc8522020-04-20 14:54:42 +02001294 out:
1295 free_trash_chunk(msg);
1296 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001297
Christopher Faulet61cc8522020-04-20 14:54:42 +02001298 error:
1299 ret = TCPCHK_EVAL_STOP;
1300 check->code = err;
1301 msg = alloc_trash_chunk();
1302 if (msg)
1303 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1304 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1305 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001306
Christopher Faulet61cc8522020-04-20 14:54:42 +02001307 wait_more_data:
1308 ret = TCPCHK_EVAL_WAIT;
1309 goto out;
1310}
Simon Horman98637e52014-06-20 12:30:16 +09001311
Christopher Faulet61cc8522020-04-20 14:54:42 +02001312/* Custom tcp-check expect function to parse and validate the MySQL initial
1313 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1314 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1315 * error occurred.
1316 */
1317static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1318{
1319 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1320}
Simon Horman98637e52014-06-20 12:30:16 +09001321
Christopher Faulet61cc8522020-04-20 14:54:42 +02001322/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1323 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1324 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1325 * an error occurred.
1326 */
1327static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1328{
1329 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001330
Christopher Faulet61cc8522020-04-20 14:54:42 +02001331 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1332 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1333 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1336}
Simon Horman98637e52014-06-20 12:30:16 +09001337
Christopher Faulet61cc8522020-04-20 14:54:42 +02001338/* Custom tcp-check expect function to parse and validate the LDAP bind response
1339 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1340 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1341 * error occurred.
1342 */
1343static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1344{
1345 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1346 enum healthcheck_status status;
1347 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001348 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001350
Christopher Faulet61cc8522020-04-20 14:54:42 +02001351 /* Check if the server speaks LDAP (ASN.1/BER)
1352 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1353 * http://tools.ietf.org/html/rfc4511
1354 */
1355 /* size of LDAPMessage */
1356 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001357
Christopher Faulet61cc8522020-04-20 14:54:42 +02001358 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1359 * messageID: 0x02 0x01 0x01: INTEGER 1
1360 * protocolOp: 0x61: bindResponse
1361 */
1362 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1363 status = HCHK_STATUS_L7RSP;
1364 desc = ist("Not LDAPv3 protocol");
1365 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001366 }
Simon Horman98637e52014-06-20 12:30:16 +09001367
Christopher Faulet61cc8522020-04-20 14:54:42 +02001368 /* size of bindResponse */
1369 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001370
Christopher Faulet61cc8522020-04-20 14:54:42 +02001371 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1372 * ldapResult: 0x0a 0x01: ENUMERATION
1373 */
1374 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1375 status = HCHK_STATUS_L7RSP;
1376 desc = ist("Not LDAPv3 protocol");
1377 goto error;
1378 }
Simon Horman98637e52014-06-20 12:30:16 +09001379
Christopher Faulet61cc8522020-04-20 14:54:42 +02001380 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1381 * resultCode
1382 */
1383 check->code = *(b_head(&check->bi) + msglen + 9);
1384 if (check->code) {
1385 status = HCHK_STATUS_L7STS;
1386 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1387 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001388 }
1389
Christopher Faulet1941bab2020-05-05 07:55:50 +02001390 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1391 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001392
Christopher Faulet61cc8522020-04-20 14:54:42 +02001393 out:
1394 free_trash_chunk(msg);
1395 return ret;
1396
1397 error:
1398 ret = TCPCHK_EVAL_STOP;
1399 msg = alloc_trash_chunk();
1400 if (msg)
1401 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1402 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1403 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001404}
1405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1407 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1408 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001409 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001411{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1413 enum healthcheck_status status;
1414 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001415 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001417
Willy Tarreaubaaee002006-06-26 02:48:02 +02001418
Christopher Faulet61cc8522020-04-20 14:54:42 +02001419 memcpy(&framesz, b_head(&check->bi), 4);
1420 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001421
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 if (!last_read && b_data(&check->bi) < (4+framesz))
1423 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001424
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425 memset(b_orig(&trash), 0, b_size(&trash));
1426 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1427 status = HCHK_STATUS_L7RSP;
1428 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1429 goto error;
1430 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001431
Christopher Faulet1941bab2020-05-05 07:55:50 +02001432 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1433 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001434
Christopher Faulet61cc8522020-04-20 14:54:42 +02001435 out:
1436 free_trash_chunk(msg);
1437 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001438
Christopher Faulet61cc8522020-04-20 14:54:42 +02001439 error:
1440 ret = TCPCHK_EVAL_STOP;
1441 msg = alloc_trash_chunk();
1442 if (msg)
1443 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1444 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1445 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001446
Christopher Faulet61cc8522020-04-20 14:54:42 +02001447 wait_more_data:
1448 ret = TCPCHK_EVAL_WAIT;
1449 goto out;
1450}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001451
Christopher Faulet61cc8522020-04-20 14:54:42 +02001452/* Custom tcp-check expect function to parse and validate the agent-check
1453 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1454 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1455 */
1456static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1457{
1458 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1459 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1460 const char *hs = NULL; /* health status */
1461 const char *as = NULL; /* admin status */
1462 const char *ps = NULL; /* performance status */
1463 const char *cs = NULL; /* maxconn */
1464 const char *err = NULL; /* first error to report */
1465 const char *wrn = NULL; /* first warning to report */
1466 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001467
Christopher Faulet61cc8522020-04-20 14:54:42 +02001468 /* We're getting an agent check response. The agent could
1469 * have been disabled in the mean time with a long check
1470 * still pending. It is important that we ignore the whole
1471 * response.
1472 */
1473 if (!(check->state & CHK_ST_ENABLED))
1474 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001475
Christopher Faulet61cc8522020-04-20 14:54:42 +02001476 /* The agent supports strings made of a single line ended by the
1477 * first CR ('\r') or LF ('\n'). This line is composed of words
1478 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1479 * line may optionally contained a description of a state change
1480 * after a sharp ('#'), which is only considered if a health state
1481 * is announced.
1482 *
1483 * Words may be composed of :
1484 * - a numeric weight suffixed by the percent character ('%').
1485 * - a health status among "up", "down", "stopped", and "fail".
1486 * - an admin status among "ready", "drain", "maint".
1487 *
1488 * These words may appear in any order. If multiple words of the
1489 * same category appear, the last one wins.
1490 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001491
Christopher Faulet61cc8522020-04-20 14:54:42 +02001492 p = b_head(&check->bi);
1493 while (*p && *p != '\n' && *p != '\r')
1494 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001495
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496 if (!*p) {
1497 if (!last_read)
1498 goto wait_more_data;
1499
1500 /* at least inform the admin that the agent is mis-behaving */
1501 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1502 goto out;
1503 }
1504
1505 *p = 0;
1506 cmd = b_head(&check->bi);
1507
1508 while (*cmd) {
1509 /* look for next word */
1510 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1511 cmd++;
1512 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001513 }
1514
Christopher Faulet61cc8522020-04-20 14:54:42 +02001515 if (*cmd == '#') {
1516 /* this is the beginning of a health status description,
1517 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001518 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001519 cmd++;
1520 while (*cmd == '\t' || *cmd == ' ')
1521 cmd++;
1522 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001523 }
1524
Christopher Faulet61cc8522020-04-20 14:54:42 +02001525 /* find the end of the word so that we have a null-terminated
1526 * word between <cmd> and <p>.
1527 */
1528 p = cmd + 1;
1529 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1530 p++;
1531 if (*p)
1532 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001533
Christopher Faulet61cc8522020-04-20 14:54:42 +02001534 /* first, health statuses */
1535 if (strcasecmp(cmd, "up") == 0) {
1536 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1537 status = HCHK_STATUS_L7OKD;
1538 hs = cmd;
1539 }
1540 else if (strcasecmp(cmd, "down") == 0) {
1541 check->server->check.health = 0;
1542 status = HCHK_STATUS_L7STS;
1543 hs = cmd;
1544 }
1545 else if (strcasecmp(cmd, "stopped") == 0) {
1546 check->server->check.health = 0;
1547 status = HCHK_STATUS_L7STS;
1548 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001549 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001550 else if (strcasecmp(cmd, "fail") == 0) {
1551 check->server->check.health = 0;
1552 status = HCHK_STATUS_L7STS;
1553 hs = cmd;
1554 }
1555 /* admin statuses */
1556 else if (strcasecmp(cmd, "ready") == 0) {
1557 as = cmd;
1558 }
1559 else if (strcasecmp(cmd, "drain") == 0) {
1560 as = cmd;
1561 }
1562 else if (strcasecmp(cmd, "maint") == 0) {
1563 as = cmd;
1564 }
1565 /* try to parse a weight here and keep the last one */
1566 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1567 ps = cmd;
1568 }
1569 /* try to parse a maxconn here */
1570 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1571 cs = cmd;
1572 }
1573 else {
1574 /* keep a copy of the first error */
1575 if (!err)
1576 err = cmd;
1577 }
1578 /* skip to next word */
1579 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001580 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001581 /* here, cmd points either to \0 or to the beginning of a
1582 * description. Skip possible leading spaces.
1583 */
1584 while (*cmd == ' ' || *cmd == '\n')
1585 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001586
Christopher Faulet61cc8522020-04-20 14:54:42 +02001587 /* First, update the admin status so that we avoid sending other
1588 * possibly useless warnings and can also update the health if
1589 * present after going back up.
1590 */
1591 if (as) {
1592 if (strcasecmp(as, "drain") == 0)
1593 srv_adm_set_drain(check->server);
1594 else if (strcasecmp(as, "maint") == 0)
1595 srv_adm_set_maint(check->server);
1596 else
1597 srv_adm_set_ready(check->server);
1598 }
Simon Horman98637e52014-06-20 12:30:16 +09001599
Christopher Faulet61cc8522020-04-20 14:54:42 +02001600 /* now change weights */
1601 if (ps) {
1602 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001603
Christopher Faulet61cc8522020-04-20 14:54:42 +02001604 msg = server_parse_weight_change_request(check->server, ps);
1605 if (!wrn || !*wrn)
1606 wrn = msg;
1607 }
Simon Horman98637e52014-06-20 12:30:16 +09001608
Christopher Faulet61cc8522020-04-20 14:54:42 +02001609 if (cs) {
1610 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001611
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001613
Christopher Faulet61cc8522020-04-20 14:54:42 +02001614 msg = server_parse_maxconn_change_request(check->server, cs);
1615 if (!wrn || !*wrn)
1616 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001617 }
1618
Christopher Faulet61cc8522020-04-20 14:54:42 +02001619 /* and finally health status */
1620 if (hs) {
1621 /* We'll report some of the warnings and errors we have
1622 * here. Down reports are critical, we leave them untouched.
1623 * Lack of report, or report of 'UP' leaves the room for
1624 * ERR first, then WARN.
1625 */
1626 const char *msg = cmd;
1627 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001628
Christopher Faulet61cc8522020-04-20 14:54:42 +02001629 if (!*msg || status == HCHK_STATUS_L7OKD) {
1630 if (err && *err)
1631 msg = err;
1632 else if (wrn && *wrn)
1633 msg = wrn;
1634 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001635
Christopher Faulet61cc8522020-04-20 14:54:42 +02001636 t = get_trash_chunk();
1637 chunk_printf(t, "via agent : %s%s%s%s",
1638 hs, *msg ? " (" : "",
1639 msg, *msg ? ")" : "");
1640 set_server_check_status(check, status, t->area);
1641 }
1642 else if (err && *err) {
1643 /* No status change but we'd like to report something odd.
1644 * Just report the current state and copy the message.
1645 */
1646 chunk_printf(&trash, "agent reports an error : %s", err);
1647 set_server_check_status(check, status/*check->status*/, trash.area);
1648 }
1649 else if (wrn && *wrn) {
1650 /* No status change but we'd like to report something odd.
1651 * Just report the current state and copy the message.
1652 */
1653 chunk_printf(&trash, "agent warns : %s", wrn);
1654 set_server_check_status(check, status/*check->status*/, trash.area);
1655 }
1656 else
1657 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001658
Christopher Faulet61cc8522020-04-20 14:54:42 +02001659 out:
1660 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001661
Christopher Faulet61cc8522020-04-20 14:54:42 +02001662 wait_more_data:
1663 ret = TCPCHK_EVAL_WAIT;
1664 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001665}
1666
Christopher Faulet61cc8522020-04-20 14:54:42 +02001667/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1668 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1669 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001670 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001671static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001672{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001673 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1674 struct tcpcheck_connect *connect = &rule->connect;
1675 struct proxy *proxy = check->proxy;
1676 struct server *s = check->server;
1677 struct task *t = check->task;
1678 struct conn_stream *cs;
1679 struct connection *conn = NULL;
1680 struct protocol *proto;
1681 struct xprt_ops *xprt;
1682 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001683
Christopher Faulet61cc8522020-04-20 14:54:42 +02001684 /* For a connect action we'll create a new connection. We may also have
1685 * to kill a previous one. But we don't want to leave *without* a
1686 * connection if we came here from the connection layer, hence with a
1687 * connection. Thus we'll proceed in the following order :
1688 * 1: close but not release previous connection (handled by the caller)
1689 * 2: try to get a new connection
1690 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001691 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001692
Christopher Faulet61cc8522020-04-20 14:54:42 +02001693 /* 2- prepare new connection */
1694 cs = cs_new(NULL);
1695 if (!cs) {
1696 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1697 tcpcheck_get_step_id(check, rule));
1698 if (rule->comment)
1699 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1700 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1701 ret = TCPCHK_EVAL_STOP;
1702 goto out;
1703 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001704
Christopher Faulet61cc8522020-04-20 14:54:42 +02001705 /* 3- release and replace the old one on success */
1706 if (check->cs) {
1707 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001708 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1709 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001710
1711 /* We may have been scheduled to run, and the I/O handler
1712 * expects to have a cs, so remove the tasklet
1713 */
1714 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1715 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001716 }
1717
Christopher Faulet61cc8522020-04-20 14:54:42 +02001718 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001719
Christopher Faulet61cc8522020-04-20 14:54:42 +02001720 check->cs = cs;
1721 conn = cs->conn;
1722 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001723
Christopher Faulet61cc8522020-04-20 14:54:42 +02001724 /* Maybe there were an older connection we were waiting on */
1725 check->wait_list.events = 0;
1726 conn->target = s ? &s->obj_type : &proxy->obj_type;
1727
1728 /* no client address */
1729 if (!sockaddr_alloc(&conn->dst)) {
1730 status = SF_ERR_RESOURCE;
1731 goto fail_check;
1732 }
1733
1734 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001735 * addr if specified on the server. otherwise, use the server addr (it
1736 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001737 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001738 *conn->dst = (is_addr(&connect->addr)
1739 ? connect->addr
1740 : (is_addr(&check->addr) ? check->addr : s->addr));
1741 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001742
Christopher Faulet61cc8522020-04-20 14:54:42 +02001743 port = 0;
1744 if (!port && connect->port)
1745 port = connect->port;
1746 if (!port && connect->port_expr) {
1747 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001748
Christopher Faulet61cc8522020-04-20 14:54:42 +02001749 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1750 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1751 connect->port_expr, SMP_T_SINT);
1752 if (smp)
1753 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001754 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001755 if (!port && is_inet_addr(&connect->addr))
1756 port = get_host_port(&connect->addr);
1757 if (!port && check->port)
1758 port = check->port;
1759 if (!port && is_inet_addr(&check->addr))
1760 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001761 if (!port) {
1762 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001763 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001764 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001765 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001766
Christopher Faulet61cc8522020-04-20 14:54:42 +02001767 xprt = ((connect->options & TCPCHK_OPT_SSL)
1768 ? xprt_get(XPRT_SSL)
1769 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001770
Christopher Faulet61cc8522020-04-20 14:54:42 +02001771 conn_prepare(conn, proto, xprt);
1772 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001773
Christopher Faulet61cc8522020-04-20 14:54:42 +02001774 status = SF_ERR_INTERNAL;
1775 if (proto && proto->connect) {
1776 struct tcpcheck_rule *next;
1777 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001778
Christopher Faulet61cc8522020-04-20 14:54:42 +02001779 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1780 flags |= CONNECT_HAS_DATA;
Christopher Faulet206368d2020-04-03 14:51:06 +02001781
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
1783 if (!next || next->action != TCPCHK_ACT_EXPECT)
1784 flags |= CONNECT_DELACK_ALWAYS;
1785 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001786 }
1787
Christopher Faulet61cc8522020-04-20 14:54:42 +02001788 if (status != SF_ERR_NONE)
1789 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001790
Christopher Faulet61cc8522020-04-20 14:54:42 +02001791 conn->flags |= CO_FL_PRIVATE;
1792 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001793
Christopher Faulet61cc8522020-04-20 14:54:42 +02001794 /* The mux may be initialized now if there isn't server attached to the
1795 * check (email alerts) or if there is a mux proto specified or if there
1796 * is no alpn.
1797 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001798 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1799 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001800 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001801
Christopher Faulet61cc8522020-04-20 14:54:42 +02001802 if (connect->mux_proto)
1803 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001804 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001805 mux_ops = check->mux_proto->mux;
1806 else {
1807 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1808 ? PROTO_MODE_HTTP
1809 : PROTO_MODE_TCP);
1810
1811 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001812 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001813 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1814 status = SF_ERR_INTERNAL;
1815 goto fail_check;
1816 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001817 }
1818
Christopher Faulet61cc8522020-04-20 14:54:42 +02001819#ifdef USE_OPENSSL
1820 if (connect->sni)
1821 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001822 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001823 ssl_sock_set_servername(conn, s->check.sni);
1824
1825 if (connect->alpn)
1826 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001827 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001828 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1829#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001830 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001831 conn->send_proxy_ofs = 1;
1832 conn->flags |= CO_FL_SOCKS4;
1833 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001834 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001835 conn->send_proxy_ofs = 1;
1836 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001837 }
1838
Christopher Faulet61cc8522020-04-20 14:54:42 +02001839 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1840 conn->send_proxy_ofs = 1;
1841 conn->flags |= CO_FL_SEND_PROXY;
1842 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001843 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001844 conn->send_proxy_ofs = 1;
1845 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001846 }
1847
Christopher Faulet61cc8522020-04-20 14:54:42 +02001848 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1849 /* Some servers don't like reset on close */
1850 fdtab[cs->conn->handle.fd].linger_risk = 0;
1851 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001852
Christopher Faulet61cc8522020-04-20 14:54:42 +02001853 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1854 if (xprt_add_hs(conn) < 0)
1855 status = SF_ERR_RESOURCE;
1856 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001857
Christopher Faulet61cc8522020-04-20 14:54:42 +02001858 fail_check:
1859 /* It can return one of :
1860 * - SF_ERR_NONE if everything's OK
1861 * - SF_ERR_SRVTO if there are no more servers
1862 * - SF_ERR_SRVCL if the connection was refused by the server
1863 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1864 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1865 * - SF_ERR_INTERNAL for any other purely internal errors
1866 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1867 * Note that we try to prevent the network stack from sending the ACK during the
1868 * connect() when a pure TCP check is used (without PROXY protocol).
1869 */
1870 switch (status) {
1871 case SF_ERR_NONE:
1872 /* we allow up to min(inter, timeout.connect) for a connection
1873 * to establish but only when timeout.check is set as it may be
1874 * to short for a full check otherwise
1875 */
1876 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001877
Christopher Faulet61cc8522020-04-20 14:54:42 +02001878 if (proxy->timeout.check && proxy->timeout.connect) {
1879 int t_con = tick_add(now_ms, proxy->timeout.connect);
1880 t->expire = tick_first(t->expire, t_con);
1881 }
1882 break;
1883 case SF_ERR_SRVTO: /* ETIMEDOUT */
1884 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1885 case SF_ERR_PRXCOND:
1886 case SF_ERR_RESOURCE:
1887 case SF_ERR_INTERNAL:
1888 chk_report_conn_err(check, errno, 0);
1889 ret = TCPCHK_EVAL_STOP;
1890 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001891 }
1892
Christopher Faulet61cc8522020-04-20 14:54:42 +02001893 /* don't do anything until the connection is established */
1894 if (conn->flags & CO_FL_WAIT_XPRT) {
1895 ret = TCPCHK_EVAL_WAIT;
1896 goto out;
1897 }
1898
1899 out:
1900 if (conn && check->result == CHK_RES_FAILED)
1901 conn->flags |= CO_FL_ERROR;
1902 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001903}
1904
Christopher Faulet61cc8522020-04-20 14:54:42 +02001905/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1906 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1907 * TCPCHK_EVAL_STOP if an error occurred.
1908 */
1909static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001910{
1911 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001912 struct tcpcheck_send *send = &rule->send;
1913 struct conn_stream *cs = check->cs;
1914 struct connection *conn = cs_conn(cs);
1915 struct buffer *tmp = NULL;
1916 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001917
Christopher Faulet61cc8522020-04-20 14:54:42 +02001918 /* reset the read & write buffer */
1919 b_reset(&check->bi);
1920 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001921
Christopher Faulet61cc8522020-04-20 14:54:42 +02001922 switch (send->type) {
1923 case TCPCHK_SEND_STRING:
1924 case TCPCHK_SEND_BINARY:
1925 if (istlen(send->data) >= b_size(&check->bo)) {
1926 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1927 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1928 tcpcheck_get_step_id(check, rule));
1929 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1930 ret = TCPCHK_EVAL_STOP;
1931 goto out;
1932 }
1933 b_putist(&check->bo, send->data);
1934 break;
1935 case TCPCHK_SEND_STRING_LF:
1936 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1937 if (!b_data(&check->bo))
1938 goto out;
1939 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001940 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001941 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001942
Christopher Faulet61cc8522020-04-20 14:54:42 +02001943 tmp = alloc_trash_chunk();
1944 if (!tmp)
1945 goto error_lf;
1946 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1947 if (!b_data(tmp))
1948 goto out;
1949 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001950 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001951 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001952 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001953 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001954 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001955 case TCPCHK_SEND_HTTP: {
1956 struct htx_sl *sl;
1957 struct ist meth, uri, vsn, clen, body;
1958 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001959
Christopher Faulet61cc8522020-04-20 14:54:42 +02001960 tmp = alloc_trash_chunk();
1961 if (!tmp)
1962 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001963
Christopher Faulet61cc8522020-04-20 14:54:42 +02001964 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1965 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1966 : http_known_methods[send->http.meth.meth]);
1967 uri = (isttest(send->http.uri) ? send->http.uri : ist("/")); // TODO: handle uri_fmt
1968 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001969
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001970 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1971 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001972 slflags |= HTX_SL_F_VER_11;
1973 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1974 if (!isttest(send->http.body))
1975 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001976
Christopher Faulet61cc8522020-04-20 14:54:42 +02001977 htx = htx_from_buf(&check->bo);
1978 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1979 if (!sl)
1980 goto error_htx;
1981 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001982 if (!http_update_host(htx, sl, uri))
1983 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001984
Christopher Faulet61cc8522020-04-20 14:54:42 +02001985 body = send->http.body; // TODO: handle body_fmt
1986 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001987
Christopher Faulet61cc8522020-04-20 14:54:42 +02001988 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
1989 !htx_add_header(htx, ist("Content-length"), clen))
1990 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001991
Christopher Faulet61cc8522020-04-20 14:54:42 +02001992 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1993 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001994 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001995
Christopher Faulet61cc8522020-04-20 14:54:42 +02001996 list_for_each_entry(hdr, &send->http.hdrs, list) {
1997 chunk_reset(tmp);
1998 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
1999 if (!b_data(tmp))
2000 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002001 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2002 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002003 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002004 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2005 if (!http_update_authority(htx, sl, hdr_value))
2006 goto error_htx;
2007 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002008 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002009
Christopher Faulet61cc8522020-04-20 14:54:42 +02002010 }
2011 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2012 chunk_reset(tmp);
2013 httpchk_build_status_header(check->server, tmp);
2014 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2015 goto error_htx;
2016 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002017
Christopher Faulet61cc8522020-04-20 14:54:42 +02002018 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
2019 (istlen(body) && !htx_add_data_atonce(htx, send->http.body)) ||
2020 !htx_add_endof(htx, HTX_BLK_EOM))
2021 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002022
Christopher Faulet61cc8522020-04-20 14:54:42 +02002023 htx_to_buf(htx, &check->bo);
2024 break;
2025 }
2026 case TCPCHK_SEND_UNDEF:
2027 /* Should never happen. */
2028 ret = TCPCHK_EVAL_STOP;
2029 goto out;
2030 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002031
Christopher Faulet6d471212020-04-22 11:09:25 +02002032
2033 if (conn->mux->snd_buf(cs, &check->bo,
2034 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002035 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002037 goto out;
2038 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002039 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002040 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002041 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2042 ret = TCPCHK_EVAL_WAIT;
2043 goto out;
2044 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002045
Christopher Faulet61cc8522020-04-20 14:54:42 +02002046 out:
2047 free_trash_chunk(tmp);
2048 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002049
Christopher Faulet61cc8522020-04-20 14:54:42 +02002050 error_htx:
2051 if (htx) {
2052 htx_reset(htx);
2053 htx_to_buf(htx, &check->bo);
2054 }
2055 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2056 tcpcheck_get_step_id(check, rule));
2057 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2058 ret = TCPCHK_EVAL_STOP;
2059 goto out;
2060
2061 error_lf:
2062 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2063 tcpcheck_get_step_id(check, rule));
2064 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2065 ret = TCPCHK_EVAL_STOP;
2066 goto out;
2067
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002068}
2069
Christopher Faulet61cc8522020-04-20 14:54:42 +02002070/* Try to reveice data before evaluting a tcp-check expect rule. Returns
2071 * TCPCHK_EVAL_WAIT if it is already subcribed on receive events or if nothing
2072 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2073 * TCPCHK_EVAL_STOP if an error occurred.
2074 */
2075static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002076{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002077 struct conn_stream *cs = check->cs;
2078 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002079 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002080 size_t max, read, cur_read = 0;
2081 int is_empty;
2082 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002083
Christopher Faulet61cc8522020-04-20 14:54:42 +02002084 if (check->wait_list.events & SUB_RETRY_RECV)
2085 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002086
Christopher Faulet61cc8522020-04-20 14:54:42 +02002087 if (cs->flags & CS_FL_EOS)
2088 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002089
Christopher Faulet61cc8522020-04-20 14:54:42 +02002090 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002091
Christopher Faulet61cc8522020-04-20 14:54:42 +02002092 /* prepare to detect if the mux needs more room */
2093 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002094
Christopher Faulet61cc8522020-04-20 14:54:42 +02002095 while ((cs->flags & CS_FL_RCV_MORE) ||
2096 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2097 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2098 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2099 cur_read += read;
2100 if (!read ||
2101 (cs->flags & CS_FL_WANT_ROOM) ||
2102 (--read_poll <= 0) ||
2103 (read < max && read >= global.tune.recv_enough))
2104 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002105 }
2106
Christopher Faulet61cc8522020-04-20 14:54:42 +02002107 end_recv:
2108 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2109 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2110 /* Report network errors only if we got no other data. Otherwise
2111 * we'll let the upper layers decide whether the response is OK
2112 * or not. It is very common that an RST sent by the server is
2113 * reported as an error just after the last data chunk.
2114 */
2115 goto stop;
2116 }
2117 if (!cur_read) {
2118 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2119 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2120 goto wait_more_data;
2121 }
2122 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002123 int status;
2124
Christopher Faulet61cc8522020-04-20 14:54:42 +02002125 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2126 tcpcheck_get_step_id(check, rule));
2127 if (rule->comment)
2128 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002129
2130 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2131 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002132 goto stop;
2133 }
2134 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002135
2136 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002137 return ret;
2138
Christopher Faulet61cc8522020-04-20 14:54:42 +02002139 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002140 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002141 goto out;
2142
2143 wait_more_data:
2144 ret = TCPCHK_EVAL_WAIT;
2145 goto out;
2146}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002147
Christopher Faulet61cc8522020-04-20 14:54:42 +02002148/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2149 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2150 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2151 * error occurred.
2152 */
2153static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, struct tcpcheck_rule *rule, int last_read)
Christopher Faulet267b01b2020-04-04 10:27:09 +02002154{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002155 struct htx *htx = htxbuf(&check->bi);
2156 struct htx_sl *sl;
2157 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002158 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002159 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002160 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002161 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002162 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002163 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002164
Christopher Faulet61cc8522020-04-20 14:54:42 +02002165 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002166
Christopher Faulet61cc8522020-04-20 14:54:42 +02002167 if (htx->flags & HTX_FL_PARSING_ERROR) {
2168 status = HCHK_STATUS_L7RSP;
2169 goto error;
2170 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002171
Christopher Faulet61cc8522020-04-20 14:54:42 +02002172 if (htx_is_empty(htx)) {
2173 if (last_read) {
2174 status = HCHK_STATUS_L7RSP;
2175 goto error;
2176 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002177 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002178 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002179
Christopher Faulet61cc8522020-04-20 14:54:42 +02002180 sl = http_get_stline(htx);
2181 check->code = sl->info.res.status;
2182
2183 if (check->server &&
2184 (check->server->proxy->options & PR_O_DISABLE404) &&
2185 (check->server->next_state != SRV_ST_STOPPED) &&
2186 (check->code == 404)) {
2187 /* 404 may be accepted as "stopping" only if the server was up */
2188 goto out;
2189 }
2190
2191 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2192 /* Make GCC happy ; initialize match to a failure state. */
2193 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002194 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002195
2196 switch (expect->type) {
2197 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002198 match = 0;
2199 for (i = 0; i < expect->codes.num; i++) {
2200 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2201 sl->info.res.status <= expect->codes.codes[i][1]) {
2202 match = 1;
2203 break;
2204 }
2205 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002206
2207 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002208 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002209 if (LIST_ISEMPTY(&expect->onerror_fmt))
2210 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002211 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002212 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002213 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2214
2215 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002216 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002217 if (LIST_ISEMPTY(&expect->onerror_fmt))
2218 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002219 break;
2220
Christopher Faulet39708192020-05-05 10:47:36 +02002221 case TCPCHK_EXPECT_HTTP_HEADER: {
2222 struct http_hdr_ctx ctx;
2223 struct ist npat, vpat, value;
2224 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2225
2226 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2227 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002228 if (!nbuf) {
2229 status = HCHK_STATUS_L7RSP;
2230 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002231 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002232 }
Christopher Faulet39708192020-05-05 10:47:36 +02002233 nbuf->data = sess_build_logline(check->sess, NULL, b_orig(nbuf), b_size(nbuf), &expect->hdr.name_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002234 if (!b_data(nbuf)) {
2235 status = HCHK_STATUS_L7RSP;
2236 desc = ist("log-format string evaluated to an empty string");
2237 goto error;
2238 }
Christopher Faulet39708192020-05-05 10:47:36 +02002239 npat = ist2(b_orig(nbuf), b_data(nbuf));
2240 }
2241 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2242 npat = expect->hdr.name;
2243
2244 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2245 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002246 if (!vbuf) {
2247 status = HCHK_STATUS_L7RSP;
2248 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002249 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002250 }
Christopher Faulet39708192020-05-05 10:47:36 +02002251 vbuf->data = sess_build_logline(check->sess, NULL, b_orig(vbuf), b_size(vbuf), &expect->hdr.value_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002252 if (!b_data(vbuf)) {
2253 status = HCHK_STATUS_L7RSP;
2254 desc = ist("log-format string evaluated to an empty string");
2255 goto error;
2256 }
Christopher Faulet39708192020-05-05 10:47:36 +02002257 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2258 }
2259 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2260 vpat = expect->hdr.value;
2261
2262 match = 0;
2263 ctx.blk = NULL;
2264 while (1) {
2265 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2266 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2267 if (!http_find_str_header(htx, npat, &ctx, full))
2268 goto end_of_match;
2269 break;
2270 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2271 if (!http_find_pfx_header(htx, npat, &ctx, full))
2272 goto end_of_match;
2273 break;
2274 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2275 if (!http_find_sfx_header(htx, npat, &ctx, full))
2276 goto end_of_match;
2277 break;
2278 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2279 if (!http_find_sub_header(htx, npat, &ctx, full))
2280 goto end_of_match;
2281 break;
2282 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2283 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2284 goto end_of_match;
2285 break;
2286 }
2287
2288 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2289 match = 1;
2290 goto end_of_match;
2291 }
2292
2293 value = ctx.value;
2294 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2295 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2296 if (isteq(value, vpat)) {
2297 match = 1;
2298 goto end_of_match;
2299 }
2300 break;
2301 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2302 if (istlen(value) < istlen(vpat))
2303 break;
2304 value = ist2(istptr(value), istlen(vpat));
2305 if (isteq(value, vpat)) {
2306 match = 1;
2307 goto end_of_match;
2308 }
2309 break;
2310 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2311 if (istlen(value) < istlen(vpat))
2312 break;
2313 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2314 if (isteq(value, vpat)) {
2315 match = 1;
2316 goto end_of_match;
2317 }
2318 break;
2319 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2320 if (isttest(istist(value, vpat))) {
2321 match = 1;
2322 goto end_of_match;
2323 }
2324 break;
2325 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2326 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2327 match = 1;
2328 goto end_of_match;
2329 }
2330 break;
2331 }
2332 }
2333
2334 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002335 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002336 if (LIST_ISEMPTY(&expect->onerror_fmt))
2337 desc = htx_sl_res_reason(sl);
2338 break;
2339 }
2340
Christopher Faulet61cc8522020-04-20 14:54:42 +02002341 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002342 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002343 case TCPCHK_EXPECT_HTTP_BODY_LF:
2344 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002345 chunk_reset(&trash);
2346 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2347 enum htx_blk_type type = htx_get_blk_type(blk);
2348
2349 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2350 break;
2351 if (type == HTX_BLK_DATA) {
2352 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2353 break;
2354 }
2355 }
2356
2357 if (!b_data(&trash)) {
2358 if (!last_read)
2359 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002360 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002361 if (LIST_ISEMPTY(&expect->onerror_fmt))
2362 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002363 goto error;
2364 }
2365
Christopher Fauletaaab0832020-05-05 15:54:22 +02002366 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2367 tmp = alloc_trash_chunk();
2368 if (!tmp) {
2369 status = HCHK_STATUS_L7RSP;
2370 desc = ist("Failed to allocate buffer to eval log-format string");
2371 goto error;
2372 }
2373 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2374 if (!b_data(tmp)) {
2375 status = HCHK_STATUS_L7RSP;
2376 desc = ist("log-format string evaluated to an empty string");
2377 goto error;
2378 }
2379 }
2380
Christopher Faulet61cc8522020-04-20 14:54:42 +02002381 if (!last_read &&
2382 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002383 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002384 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2385 ret = TCPCHK_EVAL_WAIT;
2386 goto out;
2387 }
2388
2389 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002390 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002391 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2392 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002393 else
2394 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2395
2396 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002397 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002398 if (LIST_ISEMPTY(&expect->onerror_fmt))
2399 desc = (inverse
2400 ? ist("HTTP check matched unwanted content")
2401 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002402 break;
2403
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002404
Christopher Faulet61cc8522020-04-20 14:54:42 +02002405 default:
2406 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002407 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002408 goto error;
2409 }
2410
Christopher Faulet61cc8522020-04-20 14:54:42 +02002411 /* Wait for more data on mismatch only if no minimum is defined (-1),
2412 * otherwise the absence of match is already conclusive.
2413 */
2414 if (!match && !last_read && (expect->min_recv == -1)) {
2415 ret = TCPCHK_EVAL_WAIT;
2416 goto out;
2417 }
2418
2419 if (!(match ^ inverse))
2420 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002421
2422 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002423 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002424 free_trash_chunk(nbuf);
2425 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002426 free_trash_chunk(msg);
2427 return ret;
2428
2429 error:
2430 ret = TCPCHK_EVAL_STOP;
2431 msg = alloc_trash_chunk();
2432 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002433 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002434 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2435 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002436
2437 wait_more_data:
2438 ret = TCPCHK_EVAL_WAIT;
2439 goto out;
2440}
2441
Christopher Faulet61cc8522020-04-20 14:54:42 +02002442/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2443 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2444 * if an error occurred.
2445 */
2446static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2447{
2448 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2449 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002450 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002451 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002452 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002453 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002454
Christopher Faulet61cc8522020-04-20 14:54:42 +02002455 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002456
Christopher Faulet61cc8522020-04-20 14:54:42 +02002457 /* The current expect might need more data than the previous one, check again
2458 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002459 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002460 if (!last_read) {
2461 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2462 (b_data(&check->bi) < istlen(expect->data))) {
2463 ret = TCPCHK_EVAL_WAIT;
2464 goto out;
2465 }
2466 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2467 ret = TCPCHK_EVAL_WAIT;
2468 goto out;
2469 }
2470 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002471
Christopher Faulet61cc8522020-04-20 14:54:42 +02002472 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2473 /* Make GCC happy ; initialize match to a failure state. */
2474 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002475 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002476
Christopher Faulet61cc8522020-04-20 14:54:42 +02002477 switch (expect->type) {
2478 case TCPCHK_EXPECT_STRING:
2479 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002480 match = my_memmem(b_head(&check->bi), b_data(&check->bi), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002482 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002483 match = regex_exec2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002484 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002485
Christopher Faulet67a23452020-05-05 18:10:01 +02002486 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002487 chunk_reset(&trash);
2488 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002489 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002490 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002491
2492 case TCPCHK_EXPECT_STRING_LF:
2493 case TCPCHK_EXPECT_BINARY_LF:
2494 match = 0;
2495 tmp = alloc_trash_chunk();
2496 if (!tmp) {
2497 status = HCHK_STATUS_L7RSP;
2498 desc = ist("Failed to allocate buffer to eval format string");
2499 goto error;
2500 }
2501 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2502 if (!b_data(tmp)) {
2503 status = HCHK_STATUS_L7RSP;
2504 desc = ist("log-format string evaluated to an empty string");
2505 goto error;
2506 }
2507 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2508 int len = tmp->data;
2509 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2510 status = HCHK_STATUS_L7RSP;
2511 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2512 goto error;
2513 }
2514 tmp->data = len;
2515 }
2516 if (b_data(&check->bi) < tmp->data) {
2517 if (!last_read) {
2518 ret = TCPCHK_EVAL_WAIT;
2519 goto out;
2520 }
2521 break;
2522 }
2523 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2524 break;
2525
Christopher Faulet61cc8522020-04-20 14:54:42 +02002526 case TCPCHK_EXPECT_CUSTOM:
2527 if (expect->custom)
2528 ret = expect->custom(check, rule, last_read);
2529 goto out;
2530 default:
2531 /* Should never happen. */
2532 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002533 goto out;
2534 }
2535
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002536
Christopher Faulet61cc8522020-04-20 14:54:42 +02002537 /* Wait for more data on mismatch only if no minimum is defined (-1),
2538 * otherwise the absence of match is already conclusive.
2539 */
2540 if (!match && !last_read && (expect->min_recv == -1)) {
2541 ret = TCPCHK_EVAL_WAIT;
2542 goto out;
2543 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002544
Christopher Faulet61cc8522020-04-20 14:54:42 +02002545 /* Result as expected, next rule. */
2546 if (match ^ inverse)
2547 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002548
Christopher Fauletaaab0832020-05-05 15:54:22 +02002549 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002550 /* From this point on, we matched something we did not want, this is an error state. */
2551 ret = TCPCHK_EVAL_STOP;
2552 msg = alloc_trash_chunk();
2553 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002554 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002555 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002556 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002557
Christopher Faulet61cc8522020-04-20 14:54:42 +02002558 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002559 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002560 return ret;
2561}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002562
Christopher Faulet61cc8522020-04-20 14:54:42 +02002563/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
2564 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It nevers
2565 * waits.
2566 */
2567static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2568{
2569 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2570 struct act_rule *act_rule;
2571 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002572
Christopher Faulet61cc8522020-04-20 14:54:42 +02002573 act_rule =rule->action_kw.rule;
2574 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2575 if (act_ret != ACT_RET_CONT) {
2576 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2577 tcpcheck_get_step_id(check, rule));
2578 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2579 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002580 }
2581
Christopher Faulet61cc8522020-04-20 14:54:42 +02002582 return ret;
2583}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002584
Christopher Faulet61cc8522020-04-20 14:54:42 +02002585/* Executes a tcp-check ruleset. Note that this is called both from the
2586 * connection's wake() callback and from the check scheduling task. It returns
2587 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2588 * presenting the risk of an fd replacement.
2589 *
2590 * Please do NOT place any return statement in this function and only leave
2591 * via the out_end_tcpcheck label after setting retcode.
2592 */
2593static int tcpcheck_main(struct check *check)
2594{
2595 struct tcpcheck_rule *rule;
2596 struct conn_stream *cs = check->cs;
2597 struct connection *conn = cs_conn(cs);
2598 int must_read = 1, last_read = 0;
2599 int ret, retcode = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002600
Christopher Faulet61cc8522020-04-20 14:54:42 +02002601 /* here, we know that the check is complete or that it failed */
2602 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002603 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002604
Christopher Faulet61cc8522020-04-20 14:54:42 +02002605 /* 1- check for connection error, if any */
2606 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2607 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002608
Christopher Faulet61cc8522020-04-20 14:54:42 +02002609 /* 2- check if we are waiting for the connection establishment. It only
2610 * happens during TCPCHK_ACT_CONNECT. */
2611 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
2612 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
2613 if (conn && (conn->flags & CO_FL_WAIT_XPRT)) {
2614 if (rule->action == TCPCHK_ACT_SEND)
2615 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2616 else if (rule->action == TCPCHK_ACT_EXPECT)
2617 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2618 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002619 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002620 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002621
2622 /* 3- check for pending outgoing data. It only happens during
2623 * TCPCHK_ACT_SEND. */
2624 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
2625 if (conn && b_data(&check->bo)) {
Christopher Faulet6d471212020-04-22 11:09:25 +02002626 ret = conn->mux->snd_buf(cs, &check->bo,
2627 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002628 if (ret <= 0) {
2629 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2630 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002631 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002632 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002633 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2634 goto out;
2635 }
2636 }
2637 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002638 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002639
Christopher Faulet61cc8522020-04-20 14:54:42 +02002640 /* 4- check if a rule must be resume. It happens if check->current_step
2641 * is defined. */
2642 else if (check->current_step)
2643 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002644
Christopher Faulet61cc8522020-04-20 14:54:42 +02002645 /* 5- It is the first evaluation. We must create a session and preset
2646 * tcp-check variables */
2647 else {
2648 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002649
Christopher Faulet61cc8522020-04-20 14:54:42 +02002650 /* First evaluation, create a session */
2651 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2652 if (!check->sess) {
2653 chunk_printf(&trash, "TCPCHK error allocating check session");
2654 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2655 goto out_end_tcpcheck;
2656 }
2657 vars_init(&check->vars, SCOPE_CHECK);
2658 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002659
Christopher Faulet61cc8522020-04-20 14:54:42 +02002660 /* Preset tcp-check variables */
2661 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2662 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002663
Christopher Faulet61cc8522020-04-20 14:54:42 +02002664 memset(&smp, 0, sizeof(smp));
2665 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2666 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002667 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002668 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002669 }
2670
Christopher Faulet61cc8522020-04-20 14:54:42 +02002671 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002672
Christopher Faulet61cc8522020-04-20 14:54:42 +02002673 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
2674 enum tcpcheck_eval_ret eval_ret;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002675
Christopher Faulet61cc8522020-04-20 14:54:42 +02002676 check->code = 0;
2677 switch (rule->action) {
2678 case TCPCHK_ACT_CONNECT:
2679 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002680
Christopher Faulet61cc8522020-04-20 14:54:42 +02002681 /* close but not release yet previous connection */
2682 if (check->cs) {
2683 cs_close(check->cs);
2684 retcode = -1; /* do not reuse the fd in the caller! */
2685 }
2686 eval_ret = tcpcheck_eval_connect(check, rule);
2687 must_read = 1; last_read = 0;
2688 break;
2689 case TCPCHK_ACT_SEND:
2690 check->current_step = rule;
2691 eval_ret = tcpcheck_eval_send(check, rule);
2692 must_read = 1;
2693 break;
2694 case TCPCHK_ACT_EXPECT:
2695 check->current_step = rule;
2696 if (must_read) {
2697 if (check->proxy->timeout.check)
2698 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002699
Christopher Faulet61cc8522020-04-20 14:54:42 +02002700 eval_ret = tcpcheck_eval_recv(check, rule);
2701 if (eval_ret == TCPCHK_EVAL_STOP)
2702 goto out_end_tcpcheck;
2703 else if (eval_ret == TCPCHK_EVAL_WAIT)
2704 goto out;
2705 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2706 must_read = 0;
2707 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002708
Christopher Faulet61cc8522020-04-20 14:54:42 +02002709 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2710 ? tcpcheck_eval_expect_http(check, rule, last_read)
2711 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002712
Christopher Faulet61cc8522020-04-20 14:54:42 +02002713 if (eval_ret == TCPCHK_EVAL_WAIT) {
2714 check->current_step = rule->expect.head;
2715 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2716 }
2717 break;
2718 case TCPCHK_ACT_ACTION_KW:
2719 /* Don't update the current step */
2720 eval_ret = tcpcheck_eval_action_kw(check, rule);
2721 break;
2722 default:
2723 /* Otherwise, just go to the next one and don't update
2724 * the current step
2725 */
2726 eval_ret = TCPCHK_EVAL_CONTINUE;
2727 break;
2728 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002729
Christopher Faulet61cc8522020-04-20 14:54:42 +02002730 switch (eval_ret) {
2731 case TCPCHK_EVAL_CONTINUE:
2732 break;
2733 case TCPCHK_EVAL_WAIT:
2734 goto out;
2735 case TCPCHK_EVAL_STOP:
2736 goto out_end_tcpcheck;
2737 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002738 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002739
Christopher Faulet61cc8522020-04-20 14:54:42 +02002740 /* All rules was evaluated */
2741 if (check->current_step) {
2742 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002743
Christopher Faulet61cc8522020-04-20 14:54:42 +02002744 if (rule->action == TCPCHK_ACT_EXPECT) {
2745 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002746 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002747
Christopher Faulet61cc8522020-04-20 14:54:42 +02002748 if (check->server &&
2749 (check->server->proxy->options & PR_O_DISABLE404) &&
2750 (check->server->next_state != SRV_ST_STOPPED) &&
2751 (check->code == 404)) {
2752 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2753 goto out_end_tcpcheck;
2754 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002755
Christopher Faulet61cc8522020-04-20 14:54:42 +02002756 msg = alloc_trash_chunk();
2757 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002758 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002759 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2760 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002761 free_trash_chunk(msg);
2762 }
2763 else if (rule->action == TCPCHK_ACT_CONNECT) {
2764 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002765 enum healthcheck_status status = HCHK_STATUS_L4OK;
2766#ifdef USE_OPENSSL
2767 if (conn && ssl_sock_is_ssl(conn))
2768 status = HCHK_STATUS_L6OK;
2769#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002770 set_server_check_status(check, status, msg);
2771 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002772 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002773 else
2774 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002775
Christopher Faulet61cc8522020-04-20 14:54:42 +02002776 out_end_tcpcheck:
2777 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2778 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002779
Christopher Faulet61cc8522020-04-20 14:54:42 +02002780 out:
2781 return retcode;
2782}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002783
Christopher Faulet14cd3162020-04-16 14:50:06 +02002784
Christopher Faulet61cc8522020-04-20 14:54:42 +02002785/**************************************************************************/
2786/************** Health-checks based on an external process ****************/
2787/**************************************************************************/
2788static struct list pid_list = LIST_HEAD_INIT(pid_list);
2789static struct pool_head *pool_head_pid_list;
2790__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002791
Christopher Faulet61cc8522020-04-20 14:54:42 +02002792struct extcheck_env {
2793 char *name; /* environment variable name */
2794 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2795};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002796
Christopher Faulet61cc8522020-04-20 14:54:42 +02002797/* environment variables memory requirement for different types of data */
2798#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2799 * such environment variables are not updatable. */
2800#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2801#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2802#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002803
Christopher Faulet61cc8522020-04-20 14:54:42 +02002804/* external checks environment variables */
2805enum {
2806 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002807
Christopher Faulet61cc8522020-04-20 14:54:42 +02002808 /* Proxy specific environment variables */
2809 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2810 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2811 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2812 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002813
Christopher Faulet61cc8522020-04-20 14:54:42 +02002814 /* Server specific environment variables */
2815 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2816 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2817 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2818 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2819 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2820 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002821
Christopher Faulet61cc8522020-04-20 14:54:42 +02002822 EXTCHK_SIZE
2823};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002824
Christopher Faulet61cc8522020-04-20 14:54:42 +02002825const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2826 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2827 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2828 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2829 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2830 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2831 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2832 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2833 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2834 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2835 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2836 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2837};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002838
Christopher Faulet61cc8522020-04-20 14:54:42 +02002839void block_sigchld(void)
2840{
2841 sigset_t set;
2842 sigemptyset(&set);
2843 sigaddset(&set, SIGCHLD);
2844 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2845}
Willy Tarreaube373152018-09-06 11:45:30 +02002846
Christopher Faulet61cc8522020-04-20 14:54:42 +02002847void unblock_sigchld(void)
2848{
2849 sigset_t set;
2850 sigemptyset(&set);
2851 sigaddset(&set, SIGCHLD);
2852 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002853}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002854
Christopher Faulet61cc8522020-04-20 14:54:42 +02002855static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002856{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002857 struct pid_list *elem;
2858 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002859
Christopher Faulet61cc8522020-04-20 14:54:42 +02002860 elem = pool_alloc(pool_head_pid_list);
2861 if (!elem)
2862 return NULL;
2863 elem->pid = pid;
2864 elem->t = t;
2865 elem->exited = 0;
2866 check->curpid = elem;
2867 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002868
Christopher Faulet61cc8522020-04-20 14:54:42 +02002869 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2870 LIST_ADD(&pid_list, &elem->list);
2871 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002872
Christopher Faulet61cc8522020-04-20 14:54:42 +02002873 return elem;
2874}
Christopher Faulete5870d82020-04-15 11:32:03 +02002875
Christopher Faulet61cc8522020-04-20 14:54:42 +02002876static void pid_list_del(struct pid_list *elem)
2877{
2878 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002879
Christopher Faulet61cc8522020-04-20 14:54:42 +02002880 if (!elem)
2881 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002882
Christopher Faulet61cc8522020-04-20 14:54:42 +02002883 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2884 LIST_DEL(&elem->list);
2885 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002886
Christopher Faulet61cc8522020-04-20 14:54:42 +02002887 if (!elem->exited)
2888 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002889
Christopher Faulet61cc8522020-04-20 14:54:42 +02002890 check = elem->t->context;
2891 check->curpid = NULL;
2892 pool_free(pool_head_pid_list, elem);
2893}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002894
Christopher Faulet61cc8522020-04-20 14:54:42 +02002895/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2896static void pid_list_expire(pid_t pid, int status)
2897{
2898 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002899
Christopher Faulet61cc8522020-04-20 14:54:42 +02002900 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2901 list_for_each_entry(elem, &pid_list, list) {
2902 if (elem->pid == pid) {
2903 elem->t->expire = now_ms;
2904 elem->status = status;
2905 elem->exited = 1;
2906 task_wakeup(elem->t, TASK_WOKEN_IO);
2907 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002908 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002909 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002910 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2911}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002912
Christopher Faulet61cc8522020-04-20 14:54:42 +02002913static void sigchld_handler(struct sig_handler *sh)
2914{
2915 pid_t pid;
2916 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002917
Christopher Faulet61cc8522020-04-20 14:54:42 +02002918 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2919 pid_list_expire(pid, status);
2920}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002921
Christopher Faulet61cc8522020-04-20 14:54:42 +02002922static int init_pid_list(void)
2923{
2924 if (pool_head_pid_list != NULL)
2925 /* Nothing to do */
2926 return 0;
2927
2928 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2929 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2930 strerror(errno));
2931 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002932 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002933
Christopher Faulet61cc8522020-04-20 14:54:42 +02002934 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2935 if (pool_head_pid_list == NULL) {
2936 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2937 strerror(errno));
2938 return 1;
2939 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002940
Christopher Faulet61cc8522020-04-20 14:54:42 +02002941 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002942}
2943
Christopher Faulet61cc8522020-04-20 14:54:42 +02002944/* helper macro to set an environment variable and jump to a specific label on failure. */
2945#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002946
Christopher Faulet61cc8522020-04-20 14:54:42 +02002947/*
2948 * helper function to allocate enough memory to store an environment variable.
2949 * It will also check that the environment variable is updatable, and silently
2950 * fail if not.
2951 */
2952static int extchk_setenv(struct check *check, int idx, const char *value)
2953{
2954 int len, ret;
2955 char *envname;
2956 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002957
Christopher Faulet61cc8522020-04-20 14:54:42 +02002958 if (idx < 0 || idx >= EXTCHK_SIZE) {
2959 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
2960 return 1;
2961 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002962
Christopher Faulet61cc8522020-04-20 14:54:42 +02002963 envname = extcheck_envs[idx].name;
2964 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002965
Christopher Faulet61cc8522020-04-20 14:54:42 +02002966 /* Check if the environment variable is already set, and silently reject
2967 * the update if this one is not updatable. */
2968 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
2969 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002970
Christopher Faulet61cc8522020-04-20 14:54:42 +02002971 /* Instead of sending NOT_USED, sending an empty value is preferable */
2972 if (strcmp(value, "NOT_USED") == 0) {
2973 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02002974 }
2975
Christopher Faulet61cc8522020-04-20 14:54:42 +02002976 len = strlen(envname) + 1;
2977 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
2978 len += strlen(value);
2979 else
2980 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002981
Christopher Faulet61cc8522020-04-20 14:54:42 +02002982 if (!check->envp[idx])
2983 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02002984
Christopher Faulet61cc8522020-04-20 14:54:42 +02002985 if (!check->envp[idx]) {
2986 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
2987 return 1;
2988 }
2989 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
2990 if (ret < 0) {
2991 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
2992 return 1;
2993 }
2994 else if (ret > len) {
2995 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
2996 return 1;
2997 }
2998 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002999}
3000
Christopher Faulet61cc8522020-04-20 14:54:42 +02003001static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003002{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003003 struct server *s = check->server;
3004 struct proxy *px = s->proxy;
3005 struct listener *listener = NULL, *l;
3006 int i;
3007 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3008 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003009
Christopher Faulet61cc8522020-04-20 14:54:42 +02003010 list_for_each_entry(l, &px->conf.listeners, by_fe)
3011 /* Use the first INET, INET6 or UNIX listener */
3012 if (l->addr.ss_family == AF_INET ||
3013 l->addr.ss_family == AF_INET6 ||
3014 l->addr.ss_family == AF_UNIX) {
3015 listener = l;
3016 break;
3017 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003018
Christopher Faulet61cc8522020-04-20 14:54:42 +02003019 check->curpid = NULL;
3020 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3021 if (!check->envp) {
3022 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3023 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003024 }
3025
Christopher Faulet61cc8522020-04-20 14:54:42 +02003026 check->argv = calloc(6, sizeof(char *));
3027 if (!check->argv) {
3028 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3029 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003030 }
3031
Christopher Faulet61cc8522020-04-20 14:54:42 +02003032 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003033
Christopher Faulet61cc8522020-04-20 14:54:42 +02003034 if (!listener) {
3035 check->argv[1] = strdup("NOT_USED");
3036 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003037 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003038 else if (listener->addr.ss_family == AF_INET ||
3039 listener->addr.ss_family == AF_INET6) {
3040 addr_to_str(&listener->addr, buf, sizeof(buf));
3041 check->argv[1] = strdup(buf);
3042 port_to_str(&listener->addr, buf, sizeof(buf));
3043 check->argv[2] = strdup(buf);
3044 }
3045 else if (listener->addr.ss_family == AF_UNIX) {
3046 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003047
Christopher Faulet61cc8522020-04-20 14:54:42 +02003048 un = (struct sockaddr_un *)&listener->addr;
3049 check->argv[1] = strdup(un->sun_path);
3050 check->argv[2] = strdup("NOT_USED");
3051 }
3052 else {
3053 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3054 goto err;
3055 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003056
Christopher Faulet61cc8522020-04-20 14:54:42 +02003057 if (!check->argv[1] || !check->argv[2]) {
3058 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3059 goto err;
3060 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003061
Christopher Faulet61cc8522020-04-20 14:54:42 +02003062 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3063 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3064 if (!check->argv[3] || !check->argv[4]) {
3065 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3066 goto err;
3067 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003068
Christopher Faulet61cc8522020-04-20 14:54:42 +02003069 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3070 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3071 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003072
Christopher Faulet61cc8522020-04-20 14:54:42 +02003073 for (i = 0; i < 5; i++) {
3074 if (!check->argv[i]) {
3075 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3076 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003077 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003078 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003079
Christopher Faulet61cc8522020-04-20 14:54:42 +02003080 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3081 /* Add proxy environment variables */
3082 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3083 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3084 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3085 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3086 /* Add server environment variables */
3087 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3088 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3089 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3090 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3091 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3092 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003093
Christopher Faulet61cc8522020-04-20 14:54:42 +02003094 /* Ensure that we don't leave any hole in check->envp */
3095 for (i = 0; i < EXTCHK_SIZE; i++)
3096 if (!check->envp[i])
3097 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003098
Christopher Faulet61cc8522020-04-20 14:54:42 +02003099 return 1;
3100err:
3101 if (check->envp) {
3102 for (i = 0; i < EXTCHK_SIZE; i++)
3103 free(check->envp[i]);
3104 free(check->envp);
3105 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003106 }
3107
Christopher Faulet61cc8522020-04-20 14:54:42 +02003108 if (check->argv) {
3109 for (i = 1; i < 5; i++)
3110 free(check->argv[i]);
3111 free(check->argv);
3112 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003113 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003114 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003115}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003116
Christopher Faulet61cc8522020-04-20 14:54:42 +02003117/*
3118 * establish a server health-check that makes use of a process.
3119 *
3120 * It can return one of :
3121 * - SF_ERR_NONE if everything's OK
3122 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3123 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3124 *
3125 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003126 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003127static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003128{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003129 char buf[256];
3130 struct check *check = t->context;
3131 struct server *s = check->server;
3132 struct proxy *px = s->proxy;
3133 int status;
3134 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003135
Christopher Faulet61cc8522020-04-20 14:54:42 +02003136 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003137
Christopher Faulet61cc8522020-04-20 14:54:42 +02003138 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003139
Christopher Faulet61cc8522020-04-20 14:54:42 +02003140 pid = fork();
3141 if (pid < 0) {
3142 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3143 (global.tune.options & GTUNE_INSECURE_FORK) ?
3144 "" : " (likely caused by missing 'insecure-fork-wanted')",
3145 strerror(errno));
3146 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003147 goto out;
3148 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003149 if (pid == 0) {
3150 /* Child */
3151 extern char **environ;
3152 struct rlimit limit;
3153 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003154
Christopher Faulet61cc8522020-04-20 14:54:42 +02003155 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3156 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003157
Christopher Faulet61cc8522020-04-20 14:54:42 +02003158 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003159
Christopher Faulet61cc8522020-04-20 14:54:42 +02003160 /* restore the initial FD limits */
3161 limit.rlim_cur = rlim_fd_cur_at_boot;
3162 limit.rlim_max = rlim_fd_max_at_boot;
3163 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3164 getrlimit(RLIMIT_NOFILE, &limit);
3165 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3166 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3167 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3168 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003169
Christopher Faulet61cc8522020-04-20 14:54:42 +02003170 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003171
Christopher Faulet61cc8522020-04-20 14:54:42 +02003172 /* Update some environment variables and command args: curconn, server addr and server port */
3173 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003174
Christopher Faulet61cc8522020-04-20 14:54:42 +02003175 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3176 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003177
Christopher Faulet61cc8522020-04-20 14:54:42 +02003178 *check->argv[4] = 0;
3179 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3180 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3181 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003182
Christopher Faulet61cc8522020-04-20 14:54:42 +02003183 haproxy_unblock_signals();
3184 execvp(px->check_command, check->argv);
3185 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3186 strerror(errno));
3187 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003188 }
3189
Christopher Faulet61cc8522020-04-20 14:54:42 +02003190 /* Parent */
3191 if (check->result == CHK_RES_UNKNOWN) {
3192 if (pid_list_add(pid, t) != NULL) {
3193 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3194
3195 if (px->timeout.check && px->timeout.connect) {
3196 int t_con = tick_add(now_ms, px->timeout.connect);
3197 t->expire = tick_first(t->expire, t_con);
3198 }
3199 status = SF_ERR_NONE;
3200 goto out;
3201 }
3202 else {
3203 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3204 }
3205 kill(pid, SIGTERM); /* process creation error */
3206 }
3207 else
3208 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3209
3210out:
3211 unblock_sigchld();
3212 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003213}
3214
Christopher Faulet61cc8522020-04-20 14:54:42 +02003215/*
3216 * manages a server health-check that uses an external process. Returns
3217 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003218 *
3219 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003220 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003221 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003222static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003223{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003224 struct check *check = context;
3225 struct server *s = check->server;
3226 int rv;
3227 int ret;
3228 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003229
Christopher Faulet61cc8522020-04-20 14:54:42 +02003230 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3231 if (!(check->state & CHK_ST_INPROGRESS)) {
3232 /* no check currently running */
3233 if (!expired) /* woke up too early */
3234 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003235
Christopher Faulet61cc8522020-04-20 14:54:42 +02003236 /* we don't send any health-checks when the proxy is
3237 * stopped, the server should not be checked or the check
3238 * is disabled.
3239 */
3240 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3241 s->proxy->state == PR_STSTOPPED)
3242 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003243
Christopher Faulet61cc8522020-04-20 14:54:42 +02003244 /* we'll initiate a new check */
3245 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003246
Christopher Faulet61cc8522020-04-20 14:54:42 +02003247 check->state |= CHK_ST_INPROGRESS;
3248
3249 ret = connect_proc_chk(t);
3250 if (ret == SF_ERR_NONE) {
3251 /* the process was forked, we allow up to min(inter,
3252 * timeout.connect) for it to report its status, but
3253 * only when timeout.check is set as it may be to short
3254 * for a full check otherwise.
3255 */
3256 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3257
3258 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3259 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3260 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003261 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003262 task_set_affinity(t, tid_bit);
3263 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003264 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003265
Christopher Faulet61cc8522020-04-20 14:54:42 +02003266 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003267
Christopher Faulet61cc8522020-04-20 14:54:42 +02003268 check->state &= ~CHK_ST_INPROGRESS;
3269 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003270
Christopher Faulet61cc8522020-04-20 14:54:42 +02003271 /* we allow up to min(inter, timeout.connect) for a connection
3272 * to establish but only when timeout.check is set
3273 * as it may be to short for a full check otherwise
3274 */
3275 while (tick_is_expired(t->expire, now_ms)) {
3276 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003277
Christopher Faulet61cc8522020-04-20 14:54:42 +02003278 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3279 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003280
Christopher Faulet61cc8522020-04-20 14:54:42 +02003281 if (s->proxy->timeout.check)
3282 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003283 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003284 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003285 else {
3286 /* there was a test running.
3287 * First, let's check whether there was an uncaught error,
3288 * which can happen on connect timeout or error.
3289 */
3290 if (check->result == CHK_RES_UNKNOWN) {
3291 /* good connection is enough for pure TCP check */
3292 struct pid_list *elem = check->curpid;
3293 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003294
Christopher Faulet61cc8522020-04-20 14:54:42 +02003295 if (elem->exited) {
3296 status = elem->status; /* Save in case the process exits between use below */
3297 if (!WIFEXITED(status))
3298 check->code = -1;
3299 else
3300 check->code = WEXITSTATUS(status);
3301 if (!WIFEXITED(status) || WEXITSTATUS(status))
3302 status = HCHK_STATUS_PROCERR;
3303 else
3304 status = HCHK_STATUS_PROCOK;
3305 } else if (expired) {
3306 status = HCHK_STATUS_PROCTOUT;
3307 ha_warning("kill %d\n", (int)elem->pid);
3308 kill(elem->pid, SIGTERM);
3309 }
3310 set_server_check_status(check, status, NULL);
3311 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003312
Christopher Faulet61cc8522020-04-20 14:54:42 +02003313 if (check->result == CHK_RES_FAILED) {
3314 /* a failure or timeout detected */
3315 check_notify_failure(check);
3316 }
3317 else if (check->result == CHK_RES_CONDPASS) {
3318 /* check is OK but asks for stopping mode */
3319 check_notify_stopping(check);
3320 }
3321 else if (check->result == CHK_RES_PASSED) {
3322 /* a success was detected */
3323 check_notify_success(check);
3324 }
3325 task_set_affinity(t, 1);
3326 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003327
Christopher Faulet61cc8522020-04-20 14:54:42 +02003328 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003329
Christopher Faulet61cc8522020-04-20 14:54:42 +02003330 rv = 0;
3331 if (global.spread_checks > 0) {
3332 rv = srv_getinter(check) * global.spread_checks / 100;
3333 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3334 }
3335 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3336 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003337
Christopher Faulet61cc8522020-04-20 14:54:42 +02003338 reschedule:
3339 while (tick_is_expired(t->expire, now_ms))
3340 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003341
Christopher Faulet61cc8522020-04-20 14:54:42 +02003342 out_unlock:
3343 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3344 return t;
3345}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003346
Baptiste Assmann248f1172018-03-01 21:49:01 +01003347
Christopher Faulet61cc8522020-04-20 14:54:42 +02003348/**************************************************************************/
3349/***************** Health-checks based on connections *********************/
3350/**************************************************************************/
3351/* This function is used only for server health-checks. It handles connection
3352 * status updates including errors. If necessary, it wakes the check task up.
3353 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3354 * connection (eg: reconnect). It relies on tcpcheck_main().
3355 */
3356static int wake_srv_chk(struct conn_stream *cs)
3357{
3358 struct connection *conn = cs->conn;
3359 struct check *check = cs->data;
3360 struct email_alertq *q = container_of(check, typeof(*q), check);
3361 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003362
Christopher Faulet61cc8522020-04-20 14:54:42 +02003363 if (check->server)
3364 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3365 else
3366 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003367
Christopher Faulet61cc8522020-04-20 14:54:42 +02003368 /* we may have to make progress on the TCP checks */
3369 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003370
Christopher Faulet61cc8522020-04-20 14:54:42 +02003371 cs = check->cs;
3372 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003373
Christopher Faulet61cc8522020-04-20 14:54:42 +02003374 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3375 /* We may get error reports bypassing the I/O handlers, typically
3376 * the case when sending a pure TCP check which fails, then the I/O
3377 * handlers above are not called. This is completely handled by the
3378 * main processing task so let's simply wake it up. If we get here,
3379 * we expect errno to still be valid.
3380 */
3381 chk_report_conn_err(check, errno, 0);
3382 task_wakeup(check->task, TASK_WOKEN_IO);
3383 }
3384
3385 if (check->result != CHK_RES_UNKNOWN) {
3386 /* Check complete or aborted. If connection not yet closed do it
3387 * now and wake the check task up to be sure the result is
3388 * handled ASAP. */
3389 conn_sock_drain(conn);
3390 cs_close(cs);
3391 ret = -1;
3392 /* We may have been scheduled to run, and the
3393 * I/O handler expects to have a cs, so remove
3394 * the tasklet
3395 */
3396 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3397 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003398 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003399
3400 if (check->server)
3401 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003402 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003403 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003404
Christopher Faulet61cc8522020-04-20 14:54:42 +02003405 /* if a connection got replaced, we must absolutely prevent the connection
3406 * handler from touching its fd, and perform the FD polling updates ourselves
3407 */
3408 if (ret < 0)
3409 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003410
Christopher Faulet61cc8522020-04-20 14:54:42 +02003411 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003412}
3413
Christopher Faulet61cc8522020-04-20 14:54:42 +02003414/* This function checks if any I/O is wanted, and if so, attempts to do so */
3415static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003416{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003417 struct check *check = ctx;
3418 struct conn_stream *cs = check->cs;
3419 struct email_alertq *q = container_of(check, typeof(*q), check);
3420 int ret = 0;
Simon Hormanb1900d52015-01-30 11:22:54 +09003421
Christopher Faulet61cc8522020-04-20 14:54:42 +02003422 if (!(check->wait_list.events & SUB_RETRY_SEND))
3423 ret = wake_srv_chk(cs);
3424 if (ret == 0 && !(check->wait_list.events & SUB_RETRY_RECV)) {
3425 if (check->server)
3426 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3427 else
3428 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Hormanb1900d52015-01-30 11:22:54 +09003429
Christopher Faulet61cc8522020-04-20 14:54:42 +02003430 if (unlikely(check->result == CHK_RES_FAILED)) {
3431 /* collect possible new errors */
3432 if (cs->conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
3433 chk_report_conn_err(check, 0, 0);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003434
Christopher Faulet61cc8522020-04-20 14:54:42 +02003435 /* Reset the check buffer... */
3436 b_reset(&check->bi);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003437
Christopher Faulet61cc8522020-04-20 14:54:42 +02003438 /* Close the connection... We still attempt to nicely close if,
3439 * for instance, SSL needs to send a "close notify." Later, we perform
3440 * a hard close and reset the connection if some data are pending,
3441 * otherwise we end up with many TIME_WAITs and eat all the source port
3442 * range quickly. To avoid sending RSTs all the time, we first try to
3443 * drain pending data.
3444 */
3445 /* Call cs_shutr() first, to add the CO_FL_SOCK_RD_SH flag on the
3446 * connection, to make sure cs_shutw() will not lead to a shutdown()
3447 * that would provoke TIME_WAITs.
3448 */
3449 cs_shutr(cs, CS_SHR_DRAIN);
3450 cs_shutw(cs, CS_SHW_NORMAL);
Simon Hormanb1900d52015-01-30 11:22:54 +09003451
Christopher Faulet61cc8522020-04-20 14:54:42 +02003452 /* OK, let's not stay here forever */
3453 if (check->result == CHK_RES_FAILED)
3454 cs->conn->flags |= CO_FL_ERROR;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01003455
Christopher Faulet61cc8522020-04-20 14:54:42 +02003456 task_wakeup(t, TASK_WOKEN_IO);
3457 }
3458
3459 if (check->server)
3460 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3461 else
3462 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet23d86d12018-01-25 11:36:35 +01003463 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003464 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003465}
3466
Christopher Faulet61cc8522020-04-20 14:54:42 +02003467/* manages a server health-check that uses a connection. Returns
3468 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3469 *
3470 * Please do NOT place any return statement in this function and only leave
3471 * via the out_unlock label.
3472 */
3473static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003474{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003475 struct check *check = context;
3476 struct proxy *proxy = check->proxy;
3477 struct conn_stream *cs = check->cs;
3478 struct connection *conn = cs_conn(cs);
3479 int rv;
3480 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003481
Christopher Faulet61cc8522020-04-20 14:54:42 +02003482 if (check->server)
3483 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3484 if (!(check->state & CHK_ST_INPROGRESS)) {
3485 /* no check currently running */
3486 if (!expired) /* woke up too early */
3487 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003488
Christopher Faulet61cc8522020-04-20 14:54:42 +02003489 /* we don't send any health-checks when the proxy is
3490 * stopped, the server should not be checked or the check
3491 * is disabled.
3492 */
3493 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3494 proxy->state == PR_STSTOPPED)
3495 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003496
Christopher Faulet61cc8522020-04-20 14:54:42 +02003497 /* we'll initiate a new check */
3498 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003499
Christopher Faulet61cc8522020-04-20 14:54:42 +02003500 check->state |= CHK_ST_INPROGRESS;
3501 b_reset(&check->bi);
3502 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003503
Christopher Faulet61cc8522020-04-20 14:54:42 +02003504 task_set_affinity(t, tid_bit);
3505 cs = check->cs;
3506 conn = cs_conn(cs);
3507 if (!conn) {
3508 check->current_step = NULL;
3509 tcpcheck_main(check);
3510 goto out_unlock;
3511 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003512
Christopher Faulet61cc8522020-04-20 14:54:42 +02003513 conn->flags |= CO_FL_ERROR;
3514 chk_report_conn_err(check, 0, 0);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003515
Christopher Faulet61cc8522020-04-20 14:54:42 +02003516 /* here, we have seen a synchronous error, no fd was allocated */
3517 task_set_affinity(t, MAX_THREADS_MASK);
3518 if (cs) {
3519 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003520 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003521 /* We may have been scheduled to run, and the
3522 * I/O handler expects to have a cs, so remove
3523 * the tasklet
3524 */
3525 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3526 cs_destroy(cs);
3527 cs = check->cs = NULL;
3528 conn = NULL;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003529 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003530
Christopher Faulet61cc8522020-04-20 14:54:42 +02003531 check->state &= ~CHK_ST_INPROGRESS;
3532 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003533
Christopher Faulet61cc8522020-04-20 14:54:42 +02003534 /* we allow up to min(inter, timeout.connect) for a connection
3535 * to establish but only when timeout.check is set
3536 * as it may be to short for a full check otherwise
3537 */
3538 while (tick_is_expired(t->expire, now_ms)) {
3539 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003540
Christopher Faulet61cc8522020-04-20 14:54:42 +02003541 t_con = tick_add(t->expire, proxy->timeout.connect);
3542 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3543 if (proxy->timeout.check)
3544 t->expire = tick_first(t->expire, t_con);
3545 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003546 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003547 else {
3548 /* there was a test running.
3549 * First, let's check whether there was an uncaught error,
3550 * which can happen on connect timeout or error.
3551 */
3552 if (check->result == CHK_RES_UNKNOWN) {
3553 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3554 chk_report_conn_err(check, 0, expired);
3555 }
3556 else
3557 goto out_unlock; /* timeout not reached, wait again */
3558 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003559
Christopher Faulet61cc8522020-04-20 14:54:42 +02003560 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003561
Christopher Faulet61cc8522020-04-20 14:54:42 +02003562 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003563
Christopher Faulet61cc8522020-04-20 14:54:42 +02003564 if (conn && conn->xprt) {
3565 /* The check was aborted and the connection was not yet closed.
3566 * This can happen upon timeout, or when an external event such
3567 * as a failed response coupled with "observe layer7" caused the
3568 * server state to be suddenly changed.
3569 */
3570 conn_sock_drain(conn);
3571 cs_close(cs);
3572 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003573
Christopher Faulet61cc8522020-04-20 14:54:42 +02003574 if (cs) {
3575 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003576 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003577 /* We may have been scheduled to run, and the
3578 * I/O handler expects to have a cs, so remove
3579 * the tasklet
3580 */
3581 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3582 cs_destroy(cs);
3583 cs = check->cs = NULL;
3584 conn = NULL;
3585 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003586
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003587 if (check->sess != NULL) {
3588 vars_prune(&check->vars, check->sess, NULL);
3589 session_free(check->sess);
3590 check->sess = NULL;
3591 }
3592
Christopher Faulet61cc8522020-04-20 14:54:42 +02003593 if (check->server) {
3594 if (check->result == CHK_RES_FAILED) {
3595 /* a failure or timeout detected */
3596 check_notify_failure(check);
3597 }
3598 else if (check->result == CHK_RES_CONDPASS) {
3599 /* check is OK but asks for stopping mode */
3600 check_notify_stopping(check);
3601 }
3602 else if (check->result == CHK_RES_PASSED) {
3603 /* a success was detected */
3604 check_notify_success(check);
3605 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003606 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003607 task_set_affinity(t, MAX_THREADS_MASK);
3608 check->state &= ~CHK_ST_INPROGRESS;
3609
3610 if (check->server) {
3611 rv = 0;
3612 if (global.spread_checks > 0) {
3613 rv = srv_getinter(check) * global.spread_checks / 100;
3614 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3615 }
3616 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003617 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003618 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003619
Christopher Faulet61cc8522020-04-20 14:54:42 +02003620 reschedule:
3621 while (tick_is_expired(t->expire, now_ms))
3622 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3623 out_unlock:
3624 if (check->server)
3625 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3626 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003627}
3628
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003629
Christopher Faulet61cc8522020-04-20 14:54:42 +02003630/**************************************************************************/
3631/******************* Internals to parse tcp-check rules *******************/
3632/**************************************************************************/
3633struct action_kw_list tcp_check_keywords = {
3634 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3635};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003636
Christopher Faulet61cc8522020-04-20 14:54:42 +02003637/* Return the struct action_kw associated to a keyword */
3638static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003639{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003640 return action_lookup(&tcp_check_keywords.list, kw);
3641}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003642
Christopher Faulet61cc8522020-04-20 14:54:42 +02003643static void action_kw_tcp_check_build_list(struct buffer *chk)
3644{
3645 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003646}
3647
Christopher Faulet61cc8522020-04-20 14:54:42 +02003648/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3649 * returned on error.
3650 */
3651static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3652 struct list *rules, struct action_kw *kw,
3653 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003654{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003655 struct tcpcheck_rule *chk = NULL;
3656 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003657
Christopher Faulet61cc8522020-04-20 14:54:42 +02003658 actrule = calloc(1, sizeof(*actrule));
3659 if (!actrule) {
3660 memprintf(errmsg, "out of memory");
3661 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003662 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003663 actrule->kw = kw;
3664 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003665
Christopher Faulet61cc8522020-04-20 14:54:42 +02003666 cur_arg++;
3667 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3668 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3669 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003670 }
3671
Christopher Faulet61cc8522020-04-20 14:54:42 +02003672 chk = calloc(1, sizeof(*chk));
3673 if (!chk) {
3674 memprintf(errmsg, "out of memory");
3675 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003676 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003677 chk->action = TCPCHK_ACT_ACTION_KW;
3678 chk->action_kw.rule = actrule;
3679 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003680
3681 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003682 free(actrule);
3683 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003684}
3685
Christopher Faulet61cc8522020-04-20 14:54:42 +02003686/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3687 * returned on error.
3688 */
3689static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3690 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003691{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003692 struct tcpcheck_rule *chk = NULL;
3693 struct sockaddr_storage *sk = NULL;
3694 char *comment = NULL, *sni = NULL, *alpn = NULL;
3695 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003696 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003697 unsigned short conn_opts = 0;
3698 long port = 0;
3699 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003700
Christopher Faulet61cc8522020-04-20 14:54:42 +02003701 list_for_each_entry(chk, rules, list) {
3702 if (chk->action == TCPCHK_ACT_CONNECT)
3703 break;
3704 if (chk->action == TCPCHK_ACT_COMMENT ||
3705 chk->action == TCPCHK_ACT_ACTION_KW ||
3706 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3707 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003708
Christopher Faulet61cc8522020-04-20 14:54:42 +02003709 memprintf(errmsg, "first step MUST also be a 'connect', "
3710 "optionnaly preceded by a 'set-var', an 'unset-var' or a 'comment', "
3711 "when there is a 'connect' step in the tcp-check ruleset");
3712 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003713 }
3714
Christopher Faulet61cc8522020-04-20 14:54:42 +02003715 cur_arg++;
3716 while (*(args[cur_arg])) {
3717 if (strcmp(args[cur_arg], "default") == 0)
3718 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3719 else if (strcmp(args[cur_arg], "addr") == 0) {
3720 int port1, port2;
3721 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003722
Christopher Faulet61cc8522020-04-20 14:54:42 +02003723 if (!*(args[cur_arg+1])) {
3724 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3725 goto error;
3726 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003727
Christopher Faulet61cc8522020-04-20 14:54:42 +02003728 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3729 if (!sk) {
3730 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3731 goto error;
3732 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003733
Christopher Faulet61cc8522020-04-20 14:54:42 +02003734 proto = protocol_by_family(sk->ss_family);
3735 if (!proto || !proto->connect) {
3736 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3737 args[cur_arg]);
3738 goto error;
3739 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003740
Christopher Faulet61cc8522020-04-20 14:54:42 +02003741 if (port1 != port2) {
3742 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3743 args[cur_arg], args[cur_arg+1]);
3744 goto error;
3745 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003746
Christopher Faulet61cc8522020-04-20 14:54:42 +02003747 cur_arg++;
3748 }
3749 else if (strcmp(args[cur_arg], "port") == 0) {
3750 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003751
Christopher Faulet61cc8522020-04-20 14:54:42 +02003752 if (!*(args[cur_arg+1])) {
3753 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3754 goto error;
3755 }
3756 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003757
Christopher Faulet61cc8522020-04-20 14:54:42 +02003758 port = 0;
3759 release_sample_expr(port_expr);
3760 p = args[cur_arg]; end = p + strlen(p);
3761 port = read_uint(&p, end);
3762 if (p != end) {
3763 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003764
Christopher Faulet61cc8522020-04-20 14:54:42 +02003765 px->conf.args.ctx = ARGC_SRV;
3766 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3767 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003768
Christopher Faulet61cc8522020-04-20 14:54:42 +02003769 if (!port_expr) {
3770 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3771 goto error;
3772 }
3773 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3774 memprintf(errmsg, "error detected while parsing port expression : "
3775 " fetch method '%s' extracts information from '%s', "
3776 "none of which is available here.\n",
3777 args[cur_arg], sample_src_names(port_expr->fetch->use));
3778 goto error;
3779 }
3780 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3781 }
3782 else if (port > 65535 || port < 1) {
3783 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3784 args[cur_arg]);
3785 goto error;
3786 }
3787 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003788 else if (strcmp(args[cur_arg], "proto") == 0) {
3789 if (!*(args[cur_arg+1])) {
3790 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3791 goto error;
3792 }
3793 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3794 if (!mux_proto) {
3795 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3796 goto error;
3797 }
3798 cur_arg++;
3799 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003800 else if (strcmp(args[cur_arg], "comment") == 0) {
3801 if (!*(args[cur_arg+1])) {
3802 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3803 goto error;
3804 }
3805 cur_arg++;
3806 free(comment);
3807 comment = strdup(args[cur_arg]);
3808 if (!comment) {
3809 memprintf(errmsg, "out of memory");
3810 goto error;
3811 }
3812 }
3813 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3814 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3815 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3816 conn_opts |= TCPCHK_OPT_SOCKS4;
3817 else if (strcmp(args[cur_arg], "linger") == 0)
3818 conn_opts |= TCPCHK_OPT_LINGER;
3819#ifdef USE_OPENSSL
3820 else if (strcmp(args[cur_arg], "ssl") == 0) {
3821 px->options |= PR_O_TCPCHK_SSL;
3822 conn_opts |= TCPCHK_OPT_SSL;
3823 }
3824 else if (strcmp(args[cur_arg], "sni") == 0) {
3825 if (!*(args[cur_arg+1])) {
3826 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3827 goto error;
3828 }
3829 cur_arg++;
3830 free(sni);
3831 sni = strdup(args[cur_arg]);
3832 if (!sni) {
3833 memprintf(errmsg, "out of memory");
3834 goto error;
3835 }
3836 }
3837 else if (strcmp(args[cur_arg], "alpn") == 0) {
3838#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3839 free(alpn);
3840 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3841 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3842 goto error;
3843 }
3844 cur_arg++;
3845#else
3846 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003847 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003848#endif
3849 }
3850#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003851
Christopher Faulet61cc8522020-04-20 14:54:42 +02003852 else {
3853 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3854#ifdef USE_OPENSSL
3855 ", 'ssl', 'sni', 'alpn'"
3856#endif /* USE_OPENSSL */
3857 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3858 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003859 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003860 }
3861 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003862 }
3863
Christopher Faulet61cc8522020-04-20 14:54:42 +02003864 chk = calloc(1, sizeof(*chk));
3865 if (!chk) {
3866 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003867 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003868 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003869 chk->action = TCPCHK_ACT_CONNECT;
3870 chk->comment = comment;
3871 chk->connect.port = port;
3872 chk->connect.options = conn_opts;
3873 chk->connect.sni = sni;
3874 chk->connect.alpn = alpn;
3875 chk->connect.alpn_len= alpn_len;
3876 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003877 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003878 if (sk)
3879 chk->connect.addr = *sk;
3880 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003881
Christopher Faulet61cc8522020-04-20 14:54:42 +02003882 error:
3883 free(alpn);
3884 free(sni);
3885 free(comment);
3886 release_sample_expr(port_expr);
3887 return NULL;
3888}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003889
Christopher Faulet61cc8522020-04-20 14:54:42 +02003890/* Parses and creates a tcp-check send rule. NULL is returned on error */
3891static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3892 const char *file, int line, char **errmsg)
3893{
3894 struct tcpcheck_rule *chk = NULL;
3895 char *comment = NULL, *data = NULL;
3896 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003897
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003898 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3899 type = TCPCHK_SEND_BINARY_LF;
3900 else if (strcmp(args[cur_arg], "send-binary") == 0)
3901 type = TCPCHK_SEND_BINARY;
3902 else if (strcmp(args[cur_arg], "send-lf") == 0)
3903 type = TCPCHK_SEND_STRING_LF;
3904 else if (strcmp(args[cur_arg], "send") == 0)
3905 type = TCPCHK_SEND_STRING;
3906
Christopher Faulet61cc8522020-04-20 14:54:42 +02003907 if (!*(args[cur_arg+1])) {
3908 memprintf(errmsg, "'%s' expects a %s as argument",
3909 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003910 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003911 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003912
Christopher Faulet61cc8522020-04-20 14:54:42 +02003913 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003914
Christopher Faulet61cc8522020-04-20 14:54:42 +02003915 cur_arg += 2;
3916 while (*(args[cur_arg])) {
3917 if (strcmp(args[cur_arg], "comment") == 0) {
3918 if (!*(args[cur_arg+1])) {
3919 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3920 goto error;
3921 }
3922 cur_arg++;
3923 free(comment);
3924 comment = strdup(args[cur_arg]);
3925 if (!comment) {
3926 memprintf(errmsg, "out of memory");
3927 goto error;
3928 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003929 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003930 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003931 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003932 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003933 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003934 }
3935 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003936 }
3937
Christopher Faulet61cc8522020-04-20 14:54:42 +02003938 chk = calloc(1, sizeof(*chk));
3939 if (!chk) {
3940 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003941 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003942 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003943 chk->action = TCPCHK_ACT_SEND;
3944 chk->comment = comment;
3945 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003946
Christopher Faulet61cc8522020-04-20 14:54:42 +02003947 switch (chk->send.type) {
3948 case TCPCHK_SEND_STRING:
3949 chk->send.data = ist2(strdup(data), strlen(data));
3950 if (!isttest(chk->send.data)) {
3951 memprintf(errmsg, "out of memory");
3952 goto error;
3953 }
3954 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003955 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003956 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003957 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003958 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3959 goto error;
3960 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003961 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003962 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003963 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003964 case TCPCHK_SEND_STRING_LF:
3965 case TCPCHK_SEND_BINARY_LF:
3966 LIST_INIT(&chk->send.fmt);
3967 px->conf.args.ctx = ARGC_SRV;
3968 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3969 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3970 goto error;
3971 }
3972 break;
3973 case TCPCHK_SEND_HTTP:
3974 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003975 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003976 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003977
Christopher Faulet61cc8522020-04-20 14:54:42 +02003978 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003979
Christopher Faulet61cc8522020-04-20 14:54:42 +02003980 error:
3981 free(chk);
3982 free(comment);
3983 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003984}
3985
Christopher Faulet61cc8522020-04-20 14:54:42 +02003986/* Parses and creates a http-check send rule. NULL is returned on error */
3987static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3988 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003989{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003990 struct tcpcheck_rule *chk = NULL;
3991 struct tcpcheck_http_hdr *hdr = NULL;
3992 struct http_hdr hdrs[global.tune.max_http_hdr];
3993 char *meth = NULL, *uri = NULL, *vsn = NULL;
3994 char *body = NULL, *comment = NULL;
3995 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003996 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003997
3998 cur_arg++;
3999 while (*(args[cur_arg])) {
4000 if (strcmp(args[cur_arg], "meth") == 0) {
4001 if (!*(args[cur_arg+1])) {
4002 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4003 goto error;
4004 }
4005 cur_arg++;
4006 meth = args[cur_arg];
4007 }
4008 else if (strcmp(args[cur_arg], "uri") == 0) {
4009 if (!*(args[cur_arg+1])) {
4010 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4011 goto error;
4012 }
4013 cur_arg++;
4014 uri = args[cur_arg];
4015 // TODO: log-format uri
4016 }
Christopher Faulet907701b2020-04-28 09:37:00 +02004017 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004018 if (!*(args[cur_arg+1])) {
4019 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4020 goto error;
4021 }
4022 cur_arg++;
4023 vsn = args[cur_arg];
4024 }
4025 else if (strcmp(args[cur_arg], "hdr") == 0) {
4026 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4027 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4028 goto error;
4029 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004030
4031 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4032 if (host_hdr >= 0) {
4033 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4034 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4035 goto error;
4036 }
4037 host_hdr = i;
4038 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004039 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4040 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4041 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4042 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004043
Christopher Faulet61cc8522020-04-20 14:54:42 +02004044 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4045 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4046 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004047 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004048 cur_arg += 2;
4049 }
4050 else if (strcmp(args[cur_arg], "body") == 0) {
4051 if (!*(args[cur_arg+1])) {
4052 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4053 goto error;
4054 }
4055 cur_arg++;
4056 body = args[cur_arg];
4057 // TODO: log-format body
4058 }
4059 else if (strcmp(args[cur_arg], "comment") == 0) {
4060 if (!*(args[cur_arg+1])) {
4061 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4062 goto error;
4063 }
4064 cur_arg++;
4065 free(comment);
4066 comment = strdup(args[cur_arg]);
4067 if (!comment) {
4068 memprintf(errmsg, "out of memory");
4069 goto error;
4070 }
4071 }
4072 else {
Christopher Faulet907701b2020-04-28 09:37:00 +02004073 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'ver', 'hdr' and 'body' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02004074 args[cur_arg]);
4075 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004076 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004077 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004078 }
4079
Christopher Faulet61cc8522020-04-20 14:54:42 +02004080 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004081
Christopher Faulet61cc8522020-04-20 14:54:42 +02004082 chk = calloc(1, sizeof(*chk));
4083 if (!chk) {
4084 memprintf(errmsg, "out of memory");
4085 goto error;
4086 }
4087 chk->action = TCPCHK_ACT_SEND;
4088 chk->comment = comment; comment = NULL;
4089 chk->send.type = TCPCHK_SEND_HTTP;
4090 chk->send.http.flags = flags;
4091 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004092
Christopher Faulet61cc8522020-04-20 14:54:42 +02004093 if (meth) {
4094 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4095 chk->send.http.meth.str.area = strdup(meth);
4096 chk->send.http.meth.str.data = strlen(meth);
4097 if (!chk->send.http.meth.str.area) {
4098 memprintf(errmsg, "out of memory");
4099 goto error;
4100 }
4101 }
4102 if (uri) {
4103 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4104 if (!isttest(chk->send.http.uri)) {
4105 memprintf(errmsg, "out of memory");
4106 goto error;
4107 }
4108 }
4109 if (vsn) {
4110 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4111 if (!isttest(chk->send.http.vsn)) {
4112 memprintf(errmsg, "out of memory");
4113 goto error;
4114 }
4115 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004116 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004117 hdr = calloc(1, sizeof(*hdr));
4118 if (!hdr) {
4119 memprintf(errmsg, "out of memory");
4120 goto error;
4121 }
4122 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004123 hdr->name = istdup(hdrs[i].n);
4124 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004125 memprintf(errmsg, "out of memory");
4126 goto error;
4127 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004128
Christopher Fauletb61caf42020-04-21 10:57:42 +02004129 ist0(hdrs[i].v);
4130 if (!parse_logformat_string(istptr(hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
Christopher Faulet61cc8522020-04-20 14:54:42 +02004131 goto error;
4132 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4133 hdr = NULL;
4134 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004135
Christopher Faulet61cc8522020-04-20 14:54:42 +02004136 if (body) {
4137 chk->send.http.body = ist2(strdup(body), strlen(body));
4138 if (!isttest(chk->send.http.body)) {
4139 memprintf(errmsg, "out of memory");
4140 goto error;
4141 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004142 }
4143
Christopher Faulet61cc8522020-04-20 14:54:42 +02004144 return chk;
4145
4146 error:
4147 free_tcpcheck_http_hdr(hdr);
4148 free_tcpcheck(chk, 0);
4149 free(comment);
4150 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004151}
4152
Christopher Faulet61cc8522020-04-20 14:54:42 +02004153/* Parses and creates a http-check comment rule. NULL is returned on error */
4154static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4155 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004156{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004157 struct tcpcheck_rule *chk = NULL;
4158 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004159
Christopher Faulet61cc8522020-04-20 14:54:42 +02004160 if (!*(args[cur_arg+1])) {
4161 memprintf(errmsg, "expects a string as argument");
4162 goto error;
4163 }
4164 cur_arg++;
4165 comment = strdup(args[cur_arg]);
4166 if (!comment) {
4167 memprintf(errmsg, "out of memory");
4168 goto error;
4169 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004170
Christopher Faulet61cc8522020-04-20 14:54:42 +02004171 chk = calloc(1, sizeof(*chk));
4172 if (!chk) {
4173 memprintf(errmsg, "out of memory");
4174 goto error;
4175 }
4176 chk->action = TCPCHK_ACT_COMMENT;
4177 chk->comment = comment;
4178 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004179
Christopher Faulet61cc8522020-04-20 14:54:42 +02004180 error:
4181 free(comment);
4182 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004183}
4184
Christopher Faulet61cc8522020-04-20 14:54:42 +02004185/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4186 * on error. <proto> is set to the right protocol flags (covered by the
4187 * TCPCHK_RULES_PROTO_CHK mask).
4188 */
4189static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4190 struct list *rules, unsigned int proto,
4191 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004192{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004193 struct tcpcheck_rule *prev_check, *chk = NULL;
4194 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004195 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004196 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004197 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4198 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4199 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004200 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004201 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004202 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004203
Christopher Faulet39708192020-05-05 10:47:36 +02004204 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004205 if (!*(args[cur_arg+1])) {
4206 memprintf(errmsg, "expects at least a matching pattern as arguments");
4207 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004208 }
4209
Christopher Faulet61cc8522020-04-20 14:54:42 +02004210 cur_arg++;
4211 while (*(args[cur_arg])) {
4212 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004213
Christopher Faulet61cc8522020-04-20 14:54:42 +02004214 rescan:
4215 if (strcmp(args[cur_arg], "min-recv") == 0) {
4216 if (in_pattern) {
4217 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4218 goto error;
4219 }
4220 if (!*(args[cur_arg+1])) {
4221 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4222 goto error;
4223 }
4224 /* Use an signed integer here because of chksize */
4225 cur_arg++;
4226 min_recv = atol(args[cur_arg]);
4227 if (min_recv < -1 || min_recv > INT_MAX) {
4228 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4229 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004230 }
4231 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004232 else if (*(args[cur_arg]) == '!') {
4233 in_pattern = 1;
4234 while (*(args[cur_arg]) == '!') {
4235 inverse = !inverse;
4236 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004237 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004238 if (!*(args[cur_arg]))
4239 cur_arg++;
4240 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004241 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004242 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4243 if (type != TCPCHK_EXPECT_UNDEF) {
4244 memprintf(errmsg, "only on pattern expected");
4245 goto error;
4246 }
4247 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004248 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004249 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004250 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004251
Christopher Faulet61cc8522020-04-20 14:54:42 +02004252 if (!*(args[cur_arg+1])) {
4253 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4254 goto error;
4255 }
4256 cur_arg++;
4257 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004258 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004259 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4260 if (proto == TCPCHK_RULES_HTTP_CHK)
4261 goto bad_http_kw;
4262 if (type != TCPCHK_EXPECT_UNDEF) {
4263 memprintf(errmsg, "only on pattern expected");
4264 goto error;
4265 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004266 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004267
Christopher Faulet61cc8522020-04-20 14:54:42 +02004268 if (!*(args[cur_arg+1])) {
4269 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4270 goto error;
4271 }
4272 cur_arg++;
4273 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004274 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004275 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4276 if (type != TCPCHK_EXPECT_UNDEF) {
4277 memprintf(errmsg, "only on pattern expected");
4278 goto error;
4279 }
4280 if (proto != TCPCHK_RULES_HTTP_CHK)
4281 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4282 else {
4283 if (*(args[cur_arg]) != 's')
4284 goto bad_http_kw;
4285 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4286 }
4287
4288 if (!*(args[cur_arg+1])) {
4289 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4290 goto error;
4291 }
4292 cur_arg++;
4293 pattern = args[cur_arg];
4294 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004295 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4296 if (proto != TCPCHK_RULES_HTTP_CHK)
4297 goto bad_tcp_kw;
4298 if (type != TCPCHK_EXPECT_UNDEF) {
4299 memprintf(errmsg, "only on pattern expected");
4300 goto error;
4301 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004302 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004303
Christopher Faulet61cc8522020-04-20 14:54:42 +02004304 if (!*(args[cur_arg+1])) {
4305 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4306 goto error;
4307 }
4308 cur_arg++;
4309 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004310 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004311 else if (strcmp(args[cur_arg], "custom") == 0) {
4312 if (in_pattern) {
4313 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4314 goto error;
4315 }
4316 if (type != TCPCHK_EXPECT_UNDEF) {
4317 memprintf(errmsg, "only on pattern expected");
4318 goto error;
4319 }
4320 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004321 }
Christopher Faulet39708192020-05-05 10:47:36 +02004322 else if (strcmp(args[cur_arg], "header") == 0) {
4323 int orig_arg = cur_arg;
4324
4325 if (proto != TCPCHK_RULES_HTTP_CHK)
4326 goto bad_tcp_kw;
4327 if (type != TCPCHK_EXPECT_UNDEF) {
4328 memprintf(errmsg, "only on pattern expected");
4329 goto error;
4330 }
4331 type = TCPCHK_EXPECT_HTTP_HEADER;
4332
4333 /* Parse the name pattern, mandatory */
4334 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) || strcmp(args[cur_arg+1], "name") != 0) {
4335 memprintf(errmsg, "'%s' expects at the keyword name as first argument followed by a pattern",
4336 args[orig_arg]);
4337 goto error;
4338 }
4339 cur_arg += 2;
4340 if (strcmp(args[cur_arg], "-m") == 0) {
4341 if (!*(args[cur_arg+1])) {
4342 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4343 args[orig_arg], args[cur_arg]);
4344 goto error;
4345 }
4346 if (strcmp(args[cur_arg+1], "str") == 0)
4347 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4348 else if (strcmp(args[cur_arg+1], "beg") == 0)
4349 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4350 else if (strcmp(args[cur_arg+1], "end") == 0)
4351 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4352 else if (strcmp(args[cur_arg+1], "sub") == 0)
4353 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
4354 else if (strcmp(args[cur_arg+1], "reg") == 0)
4355 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
4356 else {
4357 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4358 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4359 goto error;
4360 }
4361 cur_arg += 2;
4362 }
4363 else
4364 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4365 npat = args[cur_arg];
4366
4367 if (!(*args[cur_arg+1])) {
4368 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4369 goto next;
4370 }
4371
4372 if (strcmp(args[cur_arg+1], "log-format") == 0) {
4373 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4374 memprintf(errmsg, "'%s': '%s' cannot be used with a regex matching pattern",
4375 args[orig_arg], args[cur_arg+1]);
4376 goto error;
4377 }
4378 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4379 cur_arg++;
4380 }
4381
4382 if (!(*args[cur_arg+1]) || strcmp(args[cur_arg+1], "value") != 0) {
4383 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4384 goto next;
4385 }
4386
4387 /* Parse the value pattern, optionnal */
4388 cur_arg += 2;
4389 if (strcmp(args[cur_arg], "-m") == 0) {
4390 if (!*(args[cur_arg+1])) {
4391 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4392 args[orig_arg], args[cur_arg]);
4393 goto error;
4394 }
4395 if (strcmp(args[cur_arg+1], "str") == 0)
4396 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4397 else if (strcmp(args[cur_arg+1], "beg") == 0)
4398 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4399 else if (strcmp(args[cur_arg+1], "end") == 0)
4400 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4401 else if (strcmp(args[cur_arg+1], "sub") == 0)
4402 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
4403 else if (strcmp(args[cur_arg+1], "reg") == 0)
4404 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
4405 else {
4406 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4407 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4408 goto error;
4409 }
4410 cur_arg += 2;
4411 }
4412 else
4413 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4414 vpat = args[cur_arg];
4415
4416 while (*args[cur_arg+1]) {
4417 if (strcmp(args[cur_arg+1], "log-format") == 0) {
4418 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4419 memprintf(errmsg, "'%s': '%s' cannot be used with a regex matching pattern",
4420 args[orig_arg], args[cur_arg+1]);
4421 goto error;
4422 }
4423 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
4424 }
4425 else if (strcmp(args[cur_arg+1], "full") == 0)
4426 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4427 else
4428 break;
4429 cur_arg++;
4430 }
4431 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004432 else if (strcmp(args[cur_arg], "comment") == 0) {
4433 if (in_pattern) {
4434 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4435 goto error;
4436 }
4437 if (!*(args[cur_arg+1])) {
4438 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4439 goto error;
4440 }
4441 cur_arg++;
4442 free(comment);
4443 comment = strdup(args[cur_arg]);
4444 if (!comment) {
4445 memprintf(errmsg, "out of memory");
4446 goto error;
4447 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004448 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004449 else if (strcmp(args[cur_arg], "on-success") == 0) {
4450 if (in_pattern) {
4451 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4452 goto error;
4453 }
4454 if (!*(args[cur_arg+1])) {
4455 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4456 goto error;
4457 }
4458 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004459 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004460 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004461 else if (strcmp(args[cur_arg], "on-error") == 0) {
4462 if (in_pattern) {
4463 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4464 goto error;
4465 }
4466 if (!*(args[cur_arg+1])) {
4467 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4468 goto error;
4469 }
4470 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004471 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004472 }
4473 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4474 if (in_pattern) {
4475 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4476 goto error;
4477 }
4478 if (!*(args[cur_arg+1])) {
4479 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4480 goto error;
4481 }
4482 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4483 ok_st = HCHK_STATUS_L7OKD;
4484 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4485 ok_st = HCHK_STATUS_L7OKCD;
4486 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4487 ok_st = HCHK_STATUS_L6OK;
4488 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4489 ok_st = HCHK_STATUS_L4OK;
4490 else {
4491 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4492 args[cur_arg], args[cur_arg+1]);
4493 goto error;
4494 }
4495 cur_arg++;
4496 }
4497 else if (strcmp(args[cur_arg], "error-status") == 0) {
4498 if (in_pattern) {
4499 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4500 goto error;
4501 }
4502 if (!*(args[cur_arg+1])) {
4503 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4504 goto error;
4505 }
4506 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4507 err_st = HCHK_STATUS_L7RSP;
4508 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4509 err_st = HCHK_STATUS_L7STS;
4510 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4511 err_st = HCHK_STATUS_L6RSP;
4512 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4513 err_st = HCHK_STATUS_L4CON;
4514 else {
4515 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4516 args[cur_arg], args[cur_arg+1]);
4517 goto error;
4518 }
4519 cur_arg++;
4520 }
4521 else if (strcmp(args[cur_arg], "status-code") == 0) {
4522 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004523
Christopher Faulet61cc8522020-04-20 14:54:42 +02004524 if (in_pattern) {
4525 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4526 goto error;
4527 }
4528 if (!*(args[cur_arg+1])) {
4529 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4530 goto error;
4531 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004532
Christopher Faulet61cc8522020-04-20 14:54:42 +02004533 cur_arg++;
4534 release_sample_expr(status_expr);
4535 px->conf.args.ctx = ARGC_SRV;
4536 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4537 file, line, errmsg, &px->conf.args, NULL);
4538 if (!status_expr) {
4539 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4540 goto error;
4541 }
4542 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4543 memprintf(errmsg, "error detected while parsing status-code expression : "
4544 " fetch method '%s' extracts information from '%s', "
4545 "none of which is available here.\n",
4546 args[cur_arg], sample_src_names(status_expr->fetch->use));
4547 goto error;
4548 }
4549 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4550 }
4551 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4552 if (in_pattern) {
4553 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4554 goto error;
4555 }
4556 if (!*(args[cur_arg+1])) {
4557 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4558 goto error;
4559 }
4560 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4561 tout_st = HCHK_STATUS_L7TOUT;
4562 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4563 tout_st = HCHK_STATUS_L6TOUT;
4564 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4565 tout_st = HCHK_STATUS_L4TOUT;
4566 else {
4567 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4568 args[cur_arg], args[cur_arg+1]);
4569 goto error;
4570 }
4571 cur_arg++;
4572 }
4573 else {
4574 if (proto == TCPCHK_RULES_HTTP_CHK) {
4575 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004576 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
4577 "'[!]rstatus', [!]header or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004578 }
4579 else {
4580 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004581 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4582 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004583 }
4584 goto error;
4585 }
Christopher Faulet39708192020-05-05 10:47:36 +02004586 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004587 cur_arg++;
4588 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004589
Christopher Faulet61cc8522020-04-20 14:54:42 +02004590 chk = calloc(1, sizeof(*chk));
4591 if (!chk) {
4592 memprintf(errmsg, "out of memory");
4593 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004594 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004595 chk->action = TCPCHK_ACT_EXPECT;
4596 LIST_INIT(&chk->expect.onerror_fmt);
4597 LIST_INIT(&chk->expect.onsuccess_fmt);
4598 chk->comment = comment; comment = NULL;
4599 chk->expect.type = type;
4600 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004601 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004602 chk->expect.ok_status = ok_st;
4603 chk->expect.err_status = err_st;
4604 chk->expect.tout_status = tout_st;
4605 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004606
Christopher Faulet61cc8522020-04-20 14:54:42 +02004607 if (on_success_msg) {
4608 px->conf.args.ctx = ARGC_SRV;
4609 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4610 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4611 goto error;
4612 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004613 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004614 if (on_error_msg) {
4615 px->conf.args.ctx = ARGC_SRV;
4616 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4617 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4618 goto error;
4619 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004620 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004621
Christopher Faulet61cc8522020-04-20 14:54:42 +02004622 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004623 case TCPCHK_EXPECT_HTTP_STATUS: {
4624 const char *p = pattern;
4625 unsigned int c1,c2;
4626
4627 chk->expect.codes.codes = NULL;
4628 chk->expect.codes.num = 0;
4629 while (1) {
4630 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4631 if (*p == '-') {
4632 p++;
4633 c2 = read_uint(&p, pattern + strlen(pattern));
4634 }
4635 if (c1 > c2) {
4636 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4637 goto error;
4638 }
4639
4640 chk->expect.codes.num++;
4641 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4642 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4643 if (!chk->expect.codes.codes) {
4644 memprintf(errmsg, "out of memory");
4645 goto error;
4646 }
4647 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4648 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4649
4650 if (*p == '\0')
4651 break;
4652 if (*p != ',') {
4653 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4654 goto error;
4655 }
4656 p++;
4657 }
4658 break;
4659 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004660 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004661 case TCPCHK_EXPECT_HTTP_BODY:
4662 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004663 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004664 memprintf(errmsg, "out of memory");
4665 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004666 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004667 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004668 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004669 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004670
4671 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004672 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4673 goto error;
4674 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004675 chk->expect.data.len = len;
4676 break;
4677 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004678 case TCPCHK_EXPECT_STRING_REGEX:
4679 case TCPCHK_EXPECT_BINARY_REGEX:
4680 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4681 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004682 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004683 if (!chk->expect.regex)
4684 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004685 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004686
4687 case TCPCHK_EXPECT_STRING_LF:
4688 case TCPCHK_EXPECT_BINARY_LF:
4689 case TCPCHK_EXPECT_HTTP_BODY_LF:
4690 LIST_INIT(&chk->expect.fmt);
4691 px->conf.args.ctx = ARGC_SRV;
4692 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4693 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4694 goto error;
4695 }
4696 break;
4697
Christopher Faulet39708192020-05-05 10:47:36 +02004698 case TCPCHK_EXPECT_HTTP_HEADER:
4699 if (!npat) {
4700 memprintf(errmsg, "unexpected error, undefined header name pattern");
4701 goto error;
4702 }
4703 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4704 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4705 if (!chk->expect.hdr.name_re)
4706 goto error;
4707 }
4708 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4709 px->conf.args.ctx = ARGC_SRV;
4710 LIST_INIT(&chk->expect.hdr.name_fmt);
4711 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4712 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4713 goto error;
4714 }
4715 }
4716 else {
4717 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4718 if (!isttest(chk->expect.hdr.name)) {
4719 memprintf(errmsg, "out of memory");
4720 goto error;
4721 }
4722 }
4723
4724 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4725 chk->expect.hdr.value = IST_NULL;
4726 break;
4727 }
4728
4729 if (!vpat) {
4730 memprintf(errmsg, "unexpected error, undefined header value pattern");
4731 goto error;
4732 }
4733 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4734 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4735 if (!chk->expect.hdr.value_re)
4736 goto error;
4737 }
4738 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4739 px->conf.args.ctx = ARGC_SRV;
4740 LIST_INIT(&chk->expect.hdr.value_fmt);
4741 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4742 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4743 goto error;
4744 }
4745 }
4746 else {
4747 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4748 if (!isttest(chk->expect.hdr.value)) {
4749 memprintf(errmsg, "out of memory");
4750 goto error;
4751 }
4752 }
4753
Christopher Faulet61cc8522020-04-20 14:54:42 +02004754 break;
4755 case TCPCHK_EXPECT_CUSTOM:
4756 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4757 break;
4758 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004759 memprintf(errmsg, "pattern not found");
4760 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004761 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004762
Christopher Faulet61cc8522020-04-20 14:54:42 +02004763 /* All tcp-check expect points back to the first inverse expect rule in
4764 * a chain of one or more expect rule, potentially itself.
4765 */
4766 chk->expect.head = chk;
4767 list_for_each_entry_rev(prev_check, rules, list) {
4768 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4769 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4770 chk->expect.head = prev_check;
4771 continue;
4772 }
4773 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4774 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004775 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004776 return chk;
4777
4778 error:
4779 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004780 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004781 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004782 return NULL;
4783}
4784
Christopher Faulet61cc8522020-04-20 14:54:42 +02004785/* Overwrites fields of the old http send rule with those of the new one. When
4786 * replaced, old values are freed and replaced by the new ones. New values are
4787 * not copied but transferred. At the end <new> should be empty and can be
4788 * safely released. This function never fails.
4789 */
4790static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004791{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004792 struct logformat_node *lf, *lfb;
4793 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004794
Christopher Faulet404f9192020-04-09 23:13:54 +02004795
Christopher Faulet61cc8522020-04-20 14:54:42 +02004796 if (new->send.http.meth.str.area) {
4797 free(old->send.http.meth.str.area);
4798 old->send.http.meth.meth = new->send.http.meth.meth;
4799 old->send.http.meth.str.area = new->send.http.meth.str.area;
4800 old->send.http.meth.str.data = new->send.http.meth.str.data;
4801 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004802 }
4803
Christopher Faulet61cc8522020-04-20 14:54:42 +02004804 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4805 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004806 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004807 else
4808 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4809 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4810 old->send.http.uri = new->send.http.uri;
4811 new->send.http.uri = IST_NULL;
4812 }
4813 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4814 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004815 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004816 else
4817 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4818 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4819 LIST_INIT(&old->send.http.uri_fmt);
4820 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4821 LIST_DEL(&lf->list);
4822 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4823 }
4824 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004825
Christopher Faulet61cc8522020-04-20 14:54:42 +02004826 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004827 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004828 old->send.http.vsn = new->send.http.vsn;
4829 new->send.http.vsn = IST_NULL;
4830 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004831
Christopher Faulet61cc8522020-04-20 14:54:42 +02004832 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4833 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4834 LIST_DEL(&hdr->list);
4835 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004836 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004837
4838 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4839 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004840 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004841 else
4842 free_tcpcheck_fmt(&old->send.http.body_fmt);
4843 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4844 old->send.http.body = new->send.http.body;
4845 new->send.http.body = IST_NULL;
4846 }
4847 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4848 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004849 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004850 else
4851 free_tcpcheck_fmt(&old->send.http.body_fmt);
4852 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4853 LIST_INIT(&old->send.http.body_fmt);
4854 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4855 LIST_DEL(&lf->list);
4856 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4857 }
4858 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004859}
4860
Christopher Faulet61cc8522020-04-20 14:54:42 +02004861/* Internal function used to add an http-check rule in a list during the config
4862 * parsing step. Depending on its type, and the previously inserted rules, a
4863 * specific action may be performed or an error may be reported. This functions
4864 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4865 * message.
4866 */
4867static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004868{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004869 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004870
Christopher Faulet61cc8522020-04-20 14:54:42 +02004871 /* the implicit send rule coming from an "option httpchk" line must be
4872 * merged with the first explici http-check send rule, if
4873 * any. Depdending the declaration order some tests are required.
4874 *
4875 * Some tests is also required for other kinds of http-check rules to be
4876 * sure the ruleset remains valid.
4877 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004878
Christopher Faulet61cc8522020-04-20 14:54:42 +02004879 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4880 /* Tries to add an implcit http-check send rule from an "option httpchk" line.
4881 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4882 * following tests are performed :
4883 *
4884 * 1- If there is no such rule or if it is not a send rule, the implicit send
4885 * rule is pushed in front of the ruleset
4886 *
4887 * 2- If it is another implicit send rule, it is replaced with the new one.
4888 *
4889 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
4890 * both, overwritting the old send rule (the explicit one) with info of the
4891 * new send rule (the implicit one).
4892 */
4893 r = get_first_tcpcheck_rule(rules);
4894 if (r && r->action == TCPCHK_ACT_CONNECT)
4895 r = get_next_tcpcheck_rule(rules, r);
4896 if (!r || r->action != TCPCHK_ACT_SEND)
4897 LIST_ADD(rules->list, &chk->list);
4898 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4899 LIST_DEL(&r->list);
4900 free_tcpcheck(r, 0);
4901 LIST_ADD(rules->list, &chk->list);
4902 }
4903 else {
4904 tcpcheck_overwrite_send_http_rule(r, chk);
4905 free_tcpcheck(chk, 0);
4906 }
4907 }
4908 else {
4909 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4910 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4911 * with an existing implicit send rule, if any. At the end, if there is no error,
4912 * the rule is appended to the list.
4913 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004914
Christopher Faulet61cc8522020-04-20 14:54:42 +02004915 r = get_last_tcpcheck_rule(rules);
4916 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4917 /* no error */;
4918 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4919 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4920 chk->index+1);
4921 return 0;
4922 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004923 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004924 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4925 chk->index+1);
4926 return 0;
4927 }
4928 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4929 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4930 chk->index+1);
4931 return 0;
4932 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004933
Christopher Faulet61cc8522020-04-20 14:54:42 +02004934 if (chk->action == TCPCHK_ACT_SEND) {
4935 r = get_first_tcpcheck_rule(rules);
4936 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4937 tcpcheck_overwrite_send_http_rule(r, chk);
4938 free_tcpcheck(chk, 0);
4939 LIST_DEL(&r->list);
4940 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4941 chk = r;
4942 }
4943 }
4944 LIST_ADDQ(rules->list, &chk->list);
4945 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004946 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004947}
4948
Christopher Faulet61cc8522020-04-20 14:54:42 +02004949/**************************************************************************/
4950/************************** Init/deinit checks ****************************/
4951/**************************************************************************/
4952static const char *init_check(struct check *check, int type)
4953{
4954 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004955
Christopher Faulet61cc8522020-04-20 14:54:42 +02004956 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4957 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004958
Christopher Faulet61cc8522020-04-20 14:54:42 +02004959 check->bi.area = calloc(check->bi.size, sizeof(char));
4960 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004961
Christopher Faulet61cc8522020-04-20 14:54:42 +02004962 if (!check->bi.area || !check->bo.area)
4963 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004964
Christopher Faulet61cc8522020-04-20 14:54:42 +02004965 check->wait_list.tasklet = tasklet_new();
4966 if (!check->wait_list.tasklet)
4967 return "out of memory while allocating check tasklet";
4968 check->wait_list.events = 0;
4969 check->wait_list.tasklet->process = event_srv_chk_io;
4970 check->wait_list.tasklet->context = check;
4971 return NULL;
4972}
4973
4974void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004975{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004976 task_destroy(check->task);
4977 if (check->wait_list.tasklet)
4978 tasklet_free(check->wait_list.tasklet);
4979
4980 free(check->bi.area);
4981 free(check->bo.area);
4982 if (check->cs) {
4983 free(check->cs->conn);
4984 check->cs->conn = NULL;
4985 cs_free(check->cs);
4986 check->cs = NULL;
4987 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004988}
4989
Christopher Faulet61cc8522020-04-20 14:54:42 +02004990/* manages a server health-check. Returns the time the task accepts to wait, or
4991 * TIME_ETERNITY for infinity.
4992 */
4993static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004994{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004995 struct check *check = context;
4996
4997 if (check->type == PR_O2_EXT_CHK)
4998 return process_chk_proc(t, context, state);
4999 return process_chk_conn(t, context, state);
5000
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005001}
5002
Christopher Faulet61cc8522020-04-20 14:54:42 +02005003
5004static int start_check_task(struct check *check, int mininter,
5005 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005006{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005007 struct task *t;
5008 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005009
Christopher Faulet61cc8522020-04-20 14:54:42 +02005010 if (check->type == PR_O2_EXT_CHK)
5011 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005012
Christopher Faulet61cc8522020-04-20 14:54:42 +02005013 /* task for the check */
5014 if ((t = task_new(thread_mask)) == NULL) {
5015 ha_alert("Starting [%s:%s] check: out of memory.\n",
5016 check->server->proxy->id, check->server->id);
5017 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005018 }
5019
Christopher Faulet61cc8522020-04-20 14:54:42 +02005020 check->task = t;
5021 t->process = process_chk;
5022 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005023
Christopher Faulet61cc8522020-04-20 14:54:42 +02005024 if (mininter < srv_getinter(check))
5025 mininter = srv_getinter(check);
5026
5027 if (global.max_spread_checks && mininter > global.max_spread_checks)
5028 mininter = global.max_spread_checks;
5029
5030 /* check this every ms */
5031 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5032 check->start = now;
5033 task_queue(t);
5034
5035 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005036}
5037
Christopher Faulet61cc8522020-04-20 14:54:42 +02005038/* updates the server's weight during a warmup stage. Once the final weight is
5039 * reached, the task automatically stops. Note that any server status change
5040 * must have updated s->last_change accordingly.
5041 */
5042static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005043{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005044 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005045
Christopher Faulet61cc8522020-04-20 14:54:42 +02005046 /* by default, plan on stopping the task */
5047 t->expire = TICK_ETERNITY;
5048 if ((s->next_admin & SRV_ADMF_MAINT) ||
5049 (s->next_state != SRV_ST_STARTING))
5050 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005051
Christopher Faulet61cc8522020-04-20 14:54:42 +02005052 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005053
Christopher Faulet61cc8522020-04-20 14:54:42 +02005054 /* recalculate the weights and update the state */
5055 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005056
Christopher Faulet61cc8522020-04-20 14:54:42 +02005057 /* probably that we can refill this server with a bit more connections */
5058 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005059
Christopher Faulet61cc8522020-04-20 14:54:42 +02005060 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005061
Christopher Faulet61cc8522020-04-20 14:54:42 +02005062 /* get back there in 1 second or 1/20th of the slowstart interval,
5063 * whichever is greater, resulting in small 5% steps.
5064 */
5065 if (s->next_state == SRV_ST_STARTING)
5066 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5067 return t;
5068}
5069
5070/*
5071 * Start health-check.
5072 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5073 */
5074static int start_checks()
5075{
5076
5077 struct proxy *px;
5078 struct server *s;
5079 struct task *t;
5080 int nbcheck=0, mininter=0, srvpos=0;
5081
5082 /* 0- init the dummy frontend used to create all checks sessions */
5083 init_new_proxy(&checks_fe);
5084 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5085 checks_fe.mode = PR_MODE_TCP;
5086 checks_fe.maxconn = 0;
5087 checks_fe.conn_retries = CONN_RETRIES;
5088 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5089 checks_fe.timeout.client = TICK_ETERNITY;
5090
5091 /* 1- count the checkers to run simultaneously.
5092 * We also determine the minimum interval among all of those which
5093 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5094 * will be used to spread their start-up date. Those which have
5095 * a shorter interval will start independently and will not dictate
5096 * too short an interval for all others.
5097 */
5098 for (px = proxies_list; px; px = px->next) {
5099 for (s = px->srv; s; s = s->next) {
5100 if (s->slowstart) {
5101 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5102 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5103 return ERR_ALERT | ERR_FATAL;
5104 }
5105 /* We need a warmup task that will be called when the server
5106 * state switches from down to up.
5107 */
5108 s->warmup = t;
5109 t->process = server_warmup;
5110 t->context = s;
5111 /* server can be in this state only because of */
5112 if (s->next_state == SRV_ST_STARTING)
5113 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 +02005114 }
5115
Christopher Faulet61cc8522020-04-20 14:54:42 +02005116 if (s->check.state & CHK_ST_CONFIGURED) {
5117 nbcheck++;
5118 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5119 (!mininter || mininter > srv_getinter(&s->check)))
5120 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005121 }
5122
Christopher Faulet61cc8522020-04-20 14:54:42 +02005123 if (s->agent.state & CHK_ST_CONFIGURED) {
5124 nbcheck++;
5125 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5126 (!mininter || mininter > srv_getinter(&s->agent)))
5127 mininter = srv_getinter(&s->agent);
5128 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005129 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005130 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005131
Christopher Faulet61cc8522020-04-20 14:54:42 +02005132 if (!nbcheck)
5133 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005134
Christopher Faulet61cc8522020-04-20 14:54:42 +02005135 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005136
Christopher Faulet61cc8522020-04-20 14:54:42 +02005137 /*
5138 * 2- start them as far as possible from each others. For this, we will
5139 * start them after their interval set to the min interval divided by
5140 * the number of servers, weighted by the server's position in the list.
5141 */
5142 for (px = proxies_list; px; px = px->next) {
5143 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5144 if (init_pid_list()) {
5145 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5146 return ERR_ALERT | ERR_FATAL;
5147 }
5148 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005149
Christopher Faulet61cc8522020-04-20 14:54:42 +02005150 for (s = px->srv; s; s = s->next) {
5151 /* A task for the main check */
5152 if (s->check.state & CHK_ST_CONFIGURED) {
5153 if (s->check.type == PR_O2_EXT_CHK) {
5154 if (!prepare_external_check(&s->check))
5155 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005156 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005157 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5158 return ERR_ALERT | ERR_FATAL;
5159 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005160 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005161
Christopher Faulet61cc8522020-04-20 14:54:42 +02005162 /* A task for a auxiliary agent check */
5163 if (s->agent.state & CHK_ST_CONFIGURED) {
5164 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5165 return ERR_ALERT | ERR_FATAL;
5166 }
5167 srvpos++;
5168 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005169 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005170 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005171 return 0;
5172}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005173
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005174
Christopher Faulet61cc8522020-04-20 14:54:42 +02005175/*
5176 * Return value:
5177 * the port to be used for the health check
5178 * 0 in case no port could be found for the check
5179 */
5180static int srv_check_healthcheck_port(struct check *chk)
5181{
5182 int i = 0;
5183 struct server *srv = NULL;
5184
5185 srv = chk->server;
5186
5187 /* by default, we use the health check port ocnfigured */
5188 if (chk->port > 0)
5189 return chk->port;
5190
5191 /* try to get the port from check_core.addr if check.port not set */
5192 i = get_host_port(&chk->addr);
5193 if (i > 0)
5194 return i;
5195
5196 /* try to get the port from server address */
5197 /* prevent MAPPORTS from working at this point, since checks could
5198 * not be performed in such case (MAPPORTS impose a relative ports
5199 * based on live traffic)
5200 */
5201 if (srv->flags & SRV_F_MAPPORTS)
5202 return 0;
5203
5204 i = srv->svc_port; /* by default */
5205 if (i > 0)
5206 return i;
5207
5208 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005209}
5210
Christopher Faulet61cc8522020-04-20 14:54:42 +02005211/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5212 * if an error occurred.
5213 */
5214static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005215{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005216 const char *err;
5217 struct tcpcheck_rule *r;
5218 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005219
Christopher Faulet61cc8522020-04-20 14:54:42 +02005220 if (!srv->do_check)
5221 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005222
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005223
Christopher Faulet61cc8522020-04-20 14:54:42 +02005224 /* If neither a port nor an addr was specified and no check transport
5225 * layer is forced, then the transport layer used by the checks is the
5226 * same as for the production traffic. Otherwise we use raw_sock by
5227 * default, unless one is specified.
5228 */
5229 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5230 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5231 srv->check.use_ssl = srv->use_ssl;
5232 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005233 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005234 else if (srv->check.use_ssl == 1)
5235 srv->check.xprt = xprt_get(XPRT_SSL);
5236 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005237 }
5238
Christopher Faulet12882cf2020-04-23 15:50:18 +02005239 /* Inherit the mux protocol from the server if not already defined for
5240 * the check
5241 */
5242 if (srv->mux_proto && !srv->check.mux_proto)
5243 srv->check.mux_proto = srv->mux_proto;
5244
Christopher Faulet61cc8522020-04-20 14:54:42 +02005245 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005246
Christopher Faulet61cc8522020-04-20 14:54:42 +02005247 /* We need at least a service port, a check port or the first tcp-check
5248 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5249 */
5250 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5251 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5252 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005253
Christopher Faulet61cc8522020-04-20 14:54:42 +02005254 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5255 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5256 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5257 ret |= ERR_ALERT | ERR_ABORT;
5258 goto out;
5259 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005260
Christopher Faulet61cc8522020-04-20 14:54:42 +02005261 /* search the first action (connect / send / expect) in the list */
5262 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5263 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5264 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5265 "nor tcp_check rule 'connect' with port information.\n",
5266 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5267 ret |= ERR_ALERT | ERR_ABORT;
5268 goto out;
5269 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005270
Christopher Faulet61cc8522020-04-20 14:54:42 +02005271 /* scan the tcp-check ruleset to ensure a port has been configured */
5272 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5273 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5274 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5275 "and a tcp_check rule 'connect' with no port information.\n",
5276 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5277 ret |= ERR_ALERT | ERR_ABORT;
5278 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005279 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005280 }
5281
Christopher Faulet61cc8522020-04-20 14:54:42 +02005282 init:
5283 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5284 struct tcpcheck_ruleset *rs = NULL;
5285 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5286 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005287
Christopher Faulet61cc8522020-04-20 14:54:42 +02005288 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5289 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005290
Christopher Faulet61cc8522020-04-20 14:54:42 +02005291 rs = find_tcpcheck_ruleset("*tcp-check");
5292 if (!rs) {
5293 rs = create_tcpcheck_ruleset("*tcp-check");
5294 if (rs == NULL) {
5295 ha_alert("config: %s '%s': out of memory.\n",
5296 proxy_type_str(srv->proxy), srv->proxy->id);
5297 ret |= ERR_ALERT | ERR_FATAL;
5298 goto out;
5299 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005300 }
5301
Christopher Faulet61cc8522020-04-20 14:54:42 +02005302 free_tcpcheck_vars(&rules->preset_vars);
5303 rules->list = &rs->rules;
5304 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005305 }
5306
Christopher Faulet61cc8522020-04-20 14:54:42 +02005307 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5308 if (err) {
5309 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5310 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5311 ret |= ERR_ALERT | ERR_ABORT;
5312 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005313 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005314 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5315 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005316
Christopher Faulet61cc8522020-04-20 14:54:42 +02005317 out:
5318 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005319}
5320
Christopher Faulet61cc8522020-04-20 14:54:42 +02005321/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5322 * if an error occurred.
5323 */
5324static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005325{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005326 struct tcpcheck_rule *chk;
5327 const char *err;
5328 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005329
Christopher Faulet61cc8522020-04-20 14:54:42 +02005330 if (!srv->do_agent)
5331 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005332
Christopher Faulet61cc8522020-04-20 14:54:42 +02005333 /* If there is no connect rule preceeding all send / expect rules, an
5334 * implicit one is inserted before all others.
5335 */
5336 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5337 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5338 chk = calloc(1, sizeof(*chk));
5339 if (!chk) {
5340 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5341 " to agent-check for server '%s' (out of memory).\n",
5342 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5343 ret |= ERR_ALERT | ERR_FATAL;
5344 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005345 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005346 chk->action = TCPCHK_ACT_CONNECT;
5347 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5348 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005349 }
5350
Christopher Faulete5870d82020-04-15 11:32:03 +02005351
Christopher Faulet61cc8522020-04-20 14:54:42 +02005352 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5353 if (err) {
5354 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5355 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5356 ret |= ERR_ALERT | ERR_ABORT;
5357 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005358 }
5359
Christopher Faulet61cc8522020-04-20 14:54:42 +02005360 if (!srv->agent.inter)
5361 srv->agent.inter = srv->check.inter;
5362
5363 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5364 global.maxsock++;
5365
5366 out:
5367 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005368}
5369
Christopher Faulet61cc8522020-04-20 14:54:42 +02005370/* Check tcp-check health-check configuration for the proxy <px>. */
5371static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005372{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005373 struct tcpcheck_rule *chk, *back;
5374 char *comment = NULL, *errmsg = NULL;
5375 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5376 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005377
Christopher Faulet61cc8522020-04-20 14:54:42 +02005378 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5379 deinit_proxy_tcpcheck(px);
5380 goto out;
5381 }
5382
5383 free(px->check_command);
5384 free(px->check_path);
5385 px->check_command = px->check_path = NULL;
5386
5387 if (!px->tcpcheck_rules.list) {
5388 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5389 ret |= ERR_ALERT | ERR_FATAL;
5390 goto out;
5391 }
5392
5393 /* HTTP ruleset only : */
5394 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5395 struct tcpcheck_rule *next;
5396
5397 /* move remaining implicit send rule from "option httpchk" line to the right place.
5398 * If such rule exists, it must be the first one. In this case, the rule is moved
5399 * after the first connect rule, if any. Otherwise, nothing is done.
5400 */
5401 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5402 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5403 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5404 if (next && next->action == TCPCHK_ACT_CONNECT) {
5405 LIST_DEL(&chk->list);
5406 LIST_ADD(&next->list, &chk->list);
5407 chk->index = next->index;
5408 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005409 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005410
5411 /* add implicit expect rule if the last one is a send. It is inherited from previous
5412 * versions where the http expect rule was optional. Now it is possible to chained
5413 * send/expect rules but the last expect may still be implicit.
5414 */
5415 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5416 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005417 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005418 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5419 px->conf.file, px->conf.line, &errmsg);
5420 if (!next) {
5421 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5422 "(%s).\n", px->id, errmsg);
5423 free(errmsg);
5424 ret |= ERR_ALERT | ERR_FATAL;
5425 goto out;
5426 }
5427 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5428 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005429 }
5430 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005431
5432 /* For all ruleset: */
5433
5434 /* If there is no connect rule preceeding all send / expect rules, an
5435 * implicit one is inserted before all others.
5436 */
5437 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5438 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5439 chk = calloc(1, sizeof(*chk));
5440 if (!chk) {
5441 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5442 "(out of memory).\n", px->id);
5443 ret |= ERR_ALERT | ERR_FATAL;
5444 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005445 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005446 chk->action = TCPCHK_ACT_CONNECT;
5447 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5448 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5449 }
5450
5451 /* Remove all comment rules. To do so, when a such rule is found, the
5452 * comment is assigned to the following rule(s).
5453 */
5454 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5455 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5456 free(comment);
5457 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005458 }
5459
Christopher Faulet61cc8522020-04-20 14:54:42 +02005460 prev_action = chk->action;
5461 switch (chk->action) {
5462 case TCPCHK_ACT_COMMENT:
5463 free(comment);
5464 comment = chk->comment;
5465 LIST_DEL(&chk->list);
5466 free(chk);
5467 break;
5468 case TCPCHK_ACT_CONNECT:
5469 if (!chk->comment && comment)
5470 chk->comment = strdup(comment);
5471 /* fall though */
5472 case TCPCHK_ACT_ACTION_KW:
5473 free(comment);
5474 comment = NULL;
5475 break;
5476 case TCPCHK_ACT_SEND:
5477 case TCPCHK_ACT_EXPECT:
5478 if (!chk->comment && comment)
5479 chk->comment = strdup(comment);
5480 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005481 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005482 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005483 free(comment);
5484 comment = NULL;
5485
5486 out:
5487 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005488}
5489
Christopher Faulet61cc8522020-04-20 14:54:42 +02005490void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005491{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005492 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5493 px->tcpcheck_rules.flags = 0;
5494 px->tcpcheck_rules.list = NULL;
5495}
Christopher Faulete5870d82020-04-15 11:32:03 +02005496
Christopher Faulet61cc8522020-04-20 14:54:42 +02005497static void deinit_srv_check(struct server *srv)
5498{
5499 if (srv->check.state & CHK_ST_CONFIGURED)
5500 free_check(&srv->check);
5501 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5502 srv->do_check = 0;
5503}
Christopher Faulete5870d82020-04-15 11:32:03 +02005504
Christopher Faulet61cc8522020-04-20 14:54:42 +02005505
5506static void deinit_srv_agent_check(struct server *srv)
5507{
5508 if (srv->agent.tcpcheck_rules) {
5509 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5510 free(srv->agent.tcpcheck_rules);
5511 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005512 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005513
Christopher Faulet61cc8522020-04-20 14:54:42 +02005514 if (srv->agent.state & CHK_ST_CONFIGURED)
5515 free_check(&srv->agent);
5516
5517 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5518 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005519}
5520
Christopher Faulet61cc8522020-04-20 14:54:42 +02005521static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005522{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005523 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005524 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005525 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005526
Christopher Fauletd7cee712020-04-21 13:45:00 +02005527 node = ebpt_first(&shared_tcpchecks);
5528 while (node) {
5529 next = ebpt_next(node);
5530 ebpt_delete(node);
5531 free(node->key);
5532 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005533 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5534 LIST_DEL(&r->list);
5535 free_tcpcheck(r, 0);
5536 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005537 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005538 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005539 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005540}
Christopher Faulete5870d82020-04-15 11:32:03 +02005541
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005542
Christopher Faulet61cc8522020-04-20 14:54:42 +02005543REGISTER_POST_SERVER_CHECK(init_srv_check);
5544REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5545REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5546REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005547
Christopher Faulet61cc8522020-04-20 14:54:42 +02005548REGISTER_SERVER_DEINIT(deinit_srv_check);
5549REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5550REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5551REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005552
Christopher Faulet61cc8522020-04-20 14:54:42 +02005553/**************************************************************************/
5554/****************************** Email alerts ******************************/
5555/* NOTE: It may be pertinent to use an applet to handle email alerts */
5556/* instead of a tcp-check ruleset */
5557/**************************************************************************/
5558void email_alert_free(struct email_alert *alert)
5559{
5560 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005561
Christopher Faulet61cc8522020-04-20 14:54:42 +02005562 if (!alert)
5563 return;
5564
5565 if (alert->rules.list) {
5566 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5567 LIST_DEL(&rule->list);
5568 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005569 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005570 free_tcpcheck_vars(&alert->rules.preset_vars);
5571 free(alert->rules.list);
5572 alert->rules.list = NULL;
5573 }
5574 pool_free(pool_head_email_alert, alert);
5575}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005576
Christopher Faulet61cc8522020-04-20 14:54:42 +02005577static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5578{
5579 struct check *check = context;
5580 struct email_alertq *q;
5581 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005582
Christopher Faulet61cc8522020-04-20 14:54:42 +02005583 q = container_of(check, typeof(*q), check);
5584
5585 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5586 while (1) {
5587 if (!(check->state & CHK_ST_ENABLED)) {
5588 if (LIST_ISEMPTY(&q->email_alerts)) {
5589 /* All alerts processed, queue the task */
5590 t->expire = TICK_ETERNITY;
5591 task_queue(t);
5592 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005593 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005594
5595 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5596 LIST_DEL(&alert->list);
5597 t->expire = now_ms;
5598 check->tcpcheck_rules = &alert->rules;
5599 check->status = HCHK_STATUS_INI;
5600 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005601 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005602
5603 process_chk(t, context, state);
5604 if (check->state & CHK_ST_INPROGRESS)
5605 break;
5606
5607 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5608 email_alert_free(alert);
5609 check->tcpcheck_rules = NULL;
5610 check->server = NULL;
5611 check->state &= ~CHK_ST_ENABLED;
5612 }
5613 end:
5614 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5615 return t;
5616}
5617
5618/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5619 *
5620 * The function returns 1 in success case, otherwise, it returns 0 and err is
5621 * filled.
5622 */
5623int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5624{
5625 struct mailer *mailer;
5626 struct email_alertq *queues;
5627 const char *err_str;
5628 int i = 0;
5629
5630 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5631 memprintf(err, "out of memory while allocating mailer alerts queues");
5632 goto fail_no_queue;
5633 }
5634
5635 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5636 struct email_alertq *q = &queues[i];
5637 struct check *check = &q->check;
5638 struct task *t;
5639
5640 LIST_INIT(&q->email_alerts);
5641 HA_SPIN_INIT(&q->lock);
5642 check->inter = mls->timeout.mail;
5643 check->rise = DEF_AGENT_RISETIME;
5644 check->proxy = p;
5645 check->fall = DEF_AGENT_FALLTIME;
5646 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5647 memprintf(err, "%s", err_str);
5648 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005649 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005650
5651 check->xprt = mailer->xprt;
5652 check->addr = mailer->addr;
5653 check->port = get_host_port(&mailer->addr);
5654
5655 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5656 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005657 goto error;
5658 }
5659
Christopher Faulet61cc8522020-04-20 14:54:42 +02005660 check->task = t;
5661 t->process = process_email_alert;
5662 t->context = check;
5663
5664 /* check this in one ms */
5665 t->expire = TICK_ETERNITY;
5666 check->start = now;
5667 task_queue(t);
5668 }
5669
5670 mls->users++;
5671 free(p->email_alert.mailers.name);
5672 p->email_alert.mailers.m = mls;
5673 p->email_alert.queues = queues;
5674 return 0;
5675
5676 error:
5677 for (i = 0; i < mls->count; i++) {
5678 struct email_alertq *q = &queues[i];
5679 struct check *check = &q->check;
5680
5681 free_check(check);
5682 }
5683 free(queues);
5684 fail_no_queue:
5685 return 1;
5686}
5687
5688static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5689{
5690 struct tcpcheck_rule *tcpcheck, *prev_check;
5691 struct tcpcheck_expect *expect;
5692
5693 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5694 return 0;
5695 memset(tcpcheck, 0, sizeof(*tcpcheck));
5696 tcpcheck->action = TCPCHK_ACT_EXPECT;
5697
5698 expect = &tcpcheck->expect;
5699 expect->type = TCPCHK_EXPECT_STRING;
5700 LIST_INIT(&expect->onerror_fmt);
5701 LIST_INIT(&expect->onsuccess_fmt);
5702 expect->ok_status = HCHK_STATUS_L7OKD;
5703 expect->err_status = HCHK_STATUS_L7RSP;
5704 expect->tout_status = HCHK_STATUS_L7TOUT;
5705 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005706 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005707 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5708 return 0;
5709 }
5710
5711 /* All tcp-check expect points back to the first inverse expect rule
5712 * in a chain of one or more expect rule, potentially itself.
5713 */
5714 tcpcheck->expect.head = tcpcheck;
5715 list_for_each_entry_rev(prev_check, rules->list, list) {
5716 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5717 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5718 tcpcheck->expect.head = prev_check;
5719 continue;
5720 }
5721 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5722 break;
5723 }
5724 LIST_ADDQ(rules->list, &tcpcheck->list);
5725 return 1;
5726}
5727
5728static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5729{
5730 struct tcpcheck_rule *tcpcheck;
5731 struct tcpcheck_send *send;
5732 const char *in;
5733 char *dst;
5734 int i;
5735
5736 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5737 return 0;
5738 memset(tcpcheck, 0, sizeof(*tcpcheck));
5739 tcpcheck->action = TCPCHK_ACT_SEND;
5740
5741 send = &tcpcheck->send;
5742 send->type = TCPCHK_SEND_STRING;
5743
5744 for (i = 0; strs[i]; i++)
5745 send->data.len += strlen(strs[i]);
5746
Christopher Fauletb61caf42020-04-21 10:57:42 +02005747 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005748 if (!isttest(send->data)) {
5749 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5750 return 0;
5751 }
5752
Christopher Fauletb61caf42020-04-21 10:57:42 +02005753 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005754 for (i = 0; strs[i]; i++)
5755 for (in = strs[i]; (*dst = *in++); dst++);
5756 *dst = 0;
5757
5758 LIST_ADDQ(rules->list, &tcpcheck->list);
5759 return 1;
5760}
5761
5762static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5763 struct email_alertq *q, const char *msg)
5764{
5765 struct email_alert *alert;
5766 struct tcpcheck_rule *tcpcheck;
5767 struct check *check = &q->check;
5768
5769 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5770 goto error;
5771 LIST_INIT(&alert->list);
5772 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5773 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5774 if (!alert->rules.list)
5775 goto error;
5776 LIST_INIT(alert->rules.list);
5777 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5778 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005779
Christopher Faulet61cc8522020-04-20 14:54:42 +02005780 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5781 goto error;
5782 memset(tcpcheck, 0, sizeof(*tcpcheck));
5783 tcpcheck->action = TCPCHK_ACT_CONNECT;
5784 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005785
Christopher Faulet61cc8522020-04-20 14:54:42 +02005786 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005787
Christopher Faulet61cc8522020-04-20 14:54:42 +02005788 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005789 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005790
5791 {
5792 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5793 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5794 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005795 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005796
Christopher Faulet61cc8522020-04-20 14:54:42 +02005797 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5798 goto error;
5799
5800 {
5801 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5802 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005803 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005804 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005805
5806 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5807 goto error;
5808
5809 {
5810 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5811 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005812 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005813 }
5814
Christopher Faulet61cc8522020-04-20 14:54:42 +02005815 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5816 goto error;
5817
5818 {
5819 const char * const strs[2] = { "DATA\r\n" };
5820 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005821 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005822 }
5823
5824 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5825 goto error;
5826
5827 {
5828 struct tm tm;
5829 char datestr[48];
5830 const char * const strs[18] = {
5831 "From: ", p->email_alert.from, "\r\n",
5832 "To: ", p->email_alert.to, "\r\n",
5833 "Date: ", datestr, "\r\n",
5834 "Subject: [HAproxy Alert] ", msg, "\r\n",
5835 "\r\n",
5836 msg, "\r\n",
5837 "\r\n",
5838 ".\r\n",
5839 NULL
5840 };
5841
5842 get_localtime(date.tv_sec, &tm);
5843
5844 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005845 goto error;
5846 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005847
5848 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005849 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005850 }
5851
5852 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005853 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005854
5855 {
5856 const char * const strs[2] = { "QUIT\r\n" };
5857 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5858 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005859 }
5860
Christopher Faulet61cc8522020-04-20 14:54:42 +02005861 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5862 goto error;
5863
5864 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5865 task_wakeup(check->task, TASK_WOKEN_MSG);
5866 LIST_ADDQ(&q->email_alerts, &alert->list);
5867 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5868 return 1;
5869
5870error:
5871 email_alert_free(alert);
5872 return 0;
5873}
5874
5875static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5876{
5877 int i;
5878 struct mailer *mailer;
5879
5880 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5881 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5882 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5883 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5884 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005885 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005886 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005887
Christopher Faulet61cc8522020-04-20 14:54:42 +02005888 return;
5889}
5890
5891/*
5892 * Send email alert if configured.
5893 */
5894void send_email_alert(struct server *s, int level, const char *format, ...)
5895{
5896 va_list argp;
5897 char buf[1024];
5898 int len;
5899 struct proxy *p = s->proxy;
5900
5901 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5902 return;
5903
5904 va_start(argp, format);
5905 len = vsnprintf(buf, sizeof(buf), format, argp);
5906 va_end(argp);
5907
5908 if (len < 0 || len >= sizeof(buf)) {
5909 ha_alert("Email alert [%s] could not format message\n", p->id);
5910 return;
5911 }
5912
5913 enqueue_email_alert(p, s, buf);
5914}
5915
5916/**************************************************************************/
5917/************************** Check sample fetches **************************/
5918/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005919
Christopher Faulet61cc8522020-04-20 14:54:42 +02005920static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005921 { /* END */ },
5922}};
5923
5924INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5925
5926
5927/**************************************************************************/
5928/************************ Check's parsing functions ***********************/
5929/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005930/* Parses the "tcp-check" proxy keyword */
5931static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5932 struct proxy *defpx, const char *file, int line,
5933 char **errmsg)
5934{
Christopher Faulet404f9192020-04-09 23:13:54 +02005935 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005936 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005937 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005938
5939 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5940 ret = 1;
5941
Christopher Faulet404f9192020-04-09 23:13:54 +02005942 /* Deduce the ruleset name from the proxy info */
5943 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5944 ((curpx == defpx) ? "defaults" : curpx->id),
5945 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005946
Christopher Faulet61cc8522020-04-20 14:54:42 +02005947 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005948 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005949 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005950 if (rs == NULL) {
5951 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005952 goto error;
5953 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005954 }
5955
Gaetan Rivet5301b012020-02-25 17:19:17 +01005956 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005957 if (!LIST_ISEMPTY(&rs->rules)) {
5958 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005959 index = chk->index + 1;
5960 }
5961
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005962 cur_arg = 1;
5963 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005964 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005965 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5966 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005967 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005968 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005969 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005970 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005971 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005972 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005973 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5974
5975 if (!kw) {
5976 action_kw_tcp_check_build_list(&trash);
5977 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5978 "%s%s. but got '%s'",
5979 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5980 goto error;
5981 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005982 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005983 }
5984
5985 if (!chk) {
5986 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5987 goto error;
5988 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005989 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005990
5991 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005992 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005993 LIST_ADDQ(&rs->rules, &chk->list);
5994
5995 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005996 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005997 /* Use this ruleset if the proxy already has tcp-check enabled */
5998 curpx->tcpcheck_rules.list = &rs->rules;
5999 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
6000 }
6001 else {
6002 /* mark this ruleset as unused for now */
6003 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
6004 }
6005
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006006 return ret;
6007
6008 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006009 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006010 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006011 return -1;
6012}
6013
Christopher Faulet51b129f2020-04-09 15:54:18 +02006014/* Parses the "http-check" proxy keyword */
6015static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6016 struct proxy *defpx, const char *file, int line,
6017 char **errmsg)
6018{
Christopher Faulete5870d82020-04-15 11:32:03 +02006019 struct tcpcheck_ruleset *rs = NULL;
6020 struct tcpcheck_rule *chk = NULL;
6021 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006022
6023 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6024 ret = 1;
6025
6026 cur_arg = 1;
6027 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6028 /* enable a graceful server shutdown on an HTTP 404 response */
6029 curpx->options |= PR_O_DISABLE404;
6030 if (too_many_args(1, args, errmsg, NULL))
6031 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006032 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006033 }
6034 else if (strcmp(args[cur_arg], "send-state") == 0) {
6035 /* enable emission of the apparent state of a server in HTTP checks */
6036 curpx->options2 |= PR_O2_CHK_SNDST;
6037 if (too_many_args(1, args, errmsg, NULL))
6038 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006039 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006040 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006041
Christopher Faulete5870d82020-04-15 11:32:03 +02006042 /* Deduce the ruleset name from the proxy info */
6043 chunk_printf(&trash, "*http-check-%s_%s-%d",
6044 ((curpx == defpx) ? "defaults" : curpx->id),
6045 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006046
Christopher Faulet61cc8522020-04-20 14:54:42 +02006047 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006048 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006049 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006050 if (rs == NULL) {
6051 memprintf(errmsg, "out of memory.\n");
6052 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006053 }
6054 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006055
Christopher Faulete5870d82020-04-15 11:32:03 +02006056 index = 0;
6057 if (!LIST_ISEMPTY(&rs->rules)) {
6058 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6059 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6060 index = chk->index + 1;
6061 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006062
Christopher Faulete5870d82020-04-15 11:32:03 +02006063 if (strcmp(args[cur_arg], "connect") == 0)
6064 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6065 else if (strcmp(args[cur_arg], "send") == 0)
6066 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6067 else if (strcmp(args[cur_arg], "expect") == 0)
6068 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6069 file, line, errmsg);
6070 else if (strcmp(args[cur_arg], "comment") == 0)
6071 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6072 else {
6073 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006074
Christopher Faulete5870d82020-04-15 11:32:03 +02006075 if (!kw) {
6076 action_kw_tcp_check_build_list(&trash);
6077 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6078 " 'send', 'expect'%s%s. but got '%s'",
6079 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6080 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006081 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006082 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6083 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006084
Christopher Faulete5870d82020-04-15 11:32:03 +02006085 if (!chk) {
6086 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6087 goto error;
6088 }
6089 ret = (*errmsg != NULL); /* Handle warning */
6090
6091 chk->index = index;
6092 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6093 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6094 /* Use this ruleset if the proxy already has http-check enabled */
6095 curpx->tcpcheck_rules.list = &rs->rules;
6096 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6097 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6098 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6099 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006100 goto error;
6101 }
6102 }
6103 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006104 /* mark this ruleset as unused for now */
6105 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6106 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006107 }
6108
Christopher Faulete5870d82020-04-15 11:32:03 +02006109 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006110 return ret;
6111
6112 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006113 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006114 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006115 return -1;
6116}
6117
Christopher Faulete9111b62020-04-09 18:12:08 +02006118/* Parses the "external-check" proxy keyword */
6119static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6120 struct proxy *defpx, const char *file, int line,
6121 char **errmsg)
6122{
6123 int cur_arg, ret = 0;
6124
6125 cur_arg = 1;
6126 if (!*(args[cur_arg])) {
6127 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6128 goto error;
6129 }
6130
6131 if (strcmp(args[cur_arg], "command") == 0) {
6132 if (too_many_args(2, args, errmsg, NULL))
6133 goto error;
6134 if (!*(args[cur_arg+1])) {
6135 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6136 goto error;
6137 }
6138 free(curpx->check_command);
6139 curpx->check_command = strdup(args[cur_arg+1]);
6140 }
6141 else if (strcmp(args[cur_arg], "path") == 0) {
6142 if (too_many_args(2, args, errmsg, NULL))
6143 goto error;
6144 if (!*(args[cur_arg+1])) {
6145 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6146 goto error;
6147 }
6148 free(curpx->check_path);
6149 curpx->check_path = strdup(args[cur_arg+1]);
6150 }
6151 else {
6152 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6153 args[0], args[1]);
6154 goto error;
6155 }
6156
6157 ret = (*errmsg != NULL); /* Handle warning */
6158 return ret;
6159
6160error:
6161 return -1;
6162}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006163
Christopher Faulet430e4802020-04-09 15:28:16 +02006164/* Parses the "option tcp-check" proxy keyword */
6165int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6166 const char *file, int line)
6167{
Christopher Faulet404f9192020-04-09 23:13:54 +02006168 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006169 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6170 int err_code = 0;
6171
6172 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6173 err_code |= ERR_WARN;
6174
6175 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6176 goto out;
6177
Christopher Faulet404f9192020-04-09 23:13:54 +02006178 curpx->options2 &= ~PR_O2_CHK_ANY;
6179 curpx->options2 |= PR_O2_TCPCHK_CHK;
6180
Christopher Fauletd7e63962020-04-17 20:15:59 +02006181 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006182 /* If a tcp-check rulesset is already set, do nothing */
6183 if (rules->list)
6184 goto out;
6185
6186 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6187 * get it.
6188 */
6189 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6190 goto curpx_ruleset;
6191
6192 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6193 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006194 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006195 if (rs)
6196 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006197 }
6198
Christopher Faulet404f9192020-04-09 23:13:54 +02006199 curpx_ruleset:
6200 /* Deduce the ruleset name from the proxy info */
6201 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6202 ((curpx == defpx) ? "defaults" : curpx->id),
6203 curpx->conf.file, curpx->conf.line);
6204
Christopher Faulet61cc8522020-04-20 14:54:42 +02006205 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006206 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006207 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006208 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006209 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6210 goto error;
6211 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006212 }
6213
Christopher Faulet404f9192020-04-09 23:13:54 +02006214 ruleset_found:
6215 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006216 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006217 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006218
6219 out:
6220 return err_code;
6221
6222 error:
6223 err_code |= ERR_ALERT | ERR_FATAL;
6224 goto out;
6225}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006226
6227/* Parses the "option redis-check" proxy keyword */
6228int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6229 const char *file, int line)
6230{
6231 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6232 static char *redis_res = "+PONG\r\n";
6233
6234 struct tcpcheck_ruleset *rs = NULL;
6235 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6236 struct tcpcheck_rule *chk;
6237 char *errmsg = NULL;
6238 int err_code = 0;
6239
6240 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6241 err_code |= ERR_WARN;
6242
6243 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6244 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006245
6246 curpx->options2 &= ~PR_O2_CHK_ANY;
6247 curpx->options2 |= PR_O2_TCPCHK_CHK;
6248
6249 free_tcpcheck_vars(&rules->preset_vars);
6250 rules->list = NULL;
6251 rules->flags = 0;
6252
Christopher Faulet61cc8522020-04-20 14:54:42 +02006253 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006254 if (rs)
6255 goto ruleset_found;
6256
Christopher Faulet61cc8522020-04-20 14:54:42 +02006257 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006258 if (rs == NULL) {
6259 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6260 goto error;
6261 }
6262
6263 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6264 1, curpx, &rs->rules, file, line, &errmsg);
6265 if (!chk) {
6266 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6267 goto error;
6268 }
6269 chk->index = 0;
6270 LIST_ADDQ(&rs->rules, &chk->list);
6271
6272 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6273 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006274 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006275 "on-success", "Redis server is ok",
6276 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006277 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006278 if (!chk) {
6279 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6280 goto error;
6281 }
6282 chk->index = 1;
6283 LIST_ADDQ(&rs->rules, &chk->list);
6284
Christopher Fauletd7cee712020-04-21 13:45:00 +02006285 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006286
6287 ruleset_found:
6288 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006289 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006290
6291 out:
6292 free(errmsg);
6293 return err_code;
6294
6295 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006296 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006297 err_code |= ERR_ALERT | ERR_FATAL;
6298 goto out;
6299}
6300
Christopher Faulet811f78c2020-04-01 11:10:27 +02006301
6302/* Parses the "option ssl-hello-chk" proxy keyword */
6303int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6304 const char *file, int line)
6305{
6306 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6307 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6308 *
6309 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6310 */
6311 static char sslv3_client_hello[] = {
6312 "16" /* ContentType : 0x16 = Hanshake */
6313 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6314 "0079" /* ContentLength : 0x79 bytes after this one */
6315 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6316 "000075" /* HandshakeLength : 0x75 bytes after this one */
6317 "0300" /* Hello Version : 0x0300 = v3 */
6318 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6319 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6320 "00" /* Session ID length : empty (no session ID) */
6321 "004E" /* Cipher Suite Length : 78 bytes after this one */
6322 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6323 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6324 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6325 "000D" "000E" "000F" "0010" /* various bit lengths, */
6326 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6327 "0015" "0016" "0017" "0018"
6328 "0019" "001A" "001B" "002F"
6329 "0030" "0031" "0032" "0033"
6330 "0034" "0035" "0036" "0037"
6331 "0038" "0039" "003A"
6332 "01" /* Compression Length : 0x01 = 1 byte for types */
6333 "00" /* Compression Type : 0x00 = NULL compression */
6334 };
6335
6336 struct tcpcheck_ruleset *rs = NULL;
6337 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6338 struct tcpcheck_rule *chk;
6339 char *errmsg = NULL;
6340 int err_code = 0;
6341
6342 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6343 err_code |= ERR_WARN;
6344
6345 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6346 goto out;
6347
Christopher Faulet811f78c2020-04-01 11:10:27 +02006348 curpx->options2 &= ~PR_O2_CHK_ANY;
6349 curpx->options2 |= PR_O2_TCPCHK_CHK;
6350
6351 free_tcpcheck_vars(&rules->preset_vars);
6352 rules->list = NULL;
6353 rules->flags = 0;
6354
Christopher Faulet61cc8522020-04-20 14:54:42 +02006355 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006356 if (rs)
6357 goto ruleset_found;
6358
Christopher Faulet61cc8522020-04-20 14:54:42 +02006359 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006360 if (rs == NULL) {
6361 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6362 goto error;
6363 }
6364
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006365 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006366 1, curpx, &rs->rules, file, line, &errmsg);
6367 if (!chk) {
6368 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6369 goto error;
6370 }
6371 chk->index = 0;
6372 LIST_ADDQ(&rs->rules, &chk->list);
6373
6374 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006375 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006376 "error-status", "L6RSP", "tout-status", "L6TOUT",
6377 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006378 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006379 if (!chk) {
6380 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6381 goto error;
6382 }
6383 chk->index = 1;
6384 LIST_ADDQ(&rs->rules, &chk->list);
6385
Christopher Fauletd7cee712020-04-21 13:45:00 +02006386 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006387
6388 ruleset_found:
6389 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006390 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006391
6392 out:
6393 free(errmsg);
6394 return err_code;
6395
6396 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006397 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006398 err_code |= ERR_ALERT | ERR_FATAL;
6399 goto out;
6400}
6401
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006402/* Parses the "option smtpchk" proxy keyword */
6403int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6404 const char *file, int line)
6405{
6406 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6407
6408 struct tcpcheck_ruleset *rs = NULL;
6409 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6410 struct tcpcheck_rule *chk;
6411 struct tcpcheck_var *var = NULL;
6412 char *cmd = NULL, *errmsg = NULL;
6413 int err_code = 0;
6414
6415 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6416 err_code |= ERR_WARN;
6417
6418 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6419 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006420
6421 curpx->options2 &= ~PR_O2_CHK_ANY;
6422 curpx->options2 |= PR_O2_TCPCHK_CHK;
6423
6424 free_tcpcheck_vars(&rules->preset_vars);
6425 rules->list = NULL;
6426 rules->flags = 0;
6427
6428 cur_arg += 2;
6429 if (*args[cur_arg] && *args[cur_arg+1] &&
6430 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6431 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6432 if (cmd)
6433 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6434 }
6435 else {
6436 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6437 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6438 cmd = strdup("HELO localhost");
6439 }
6440
Christopher Fauletb61caf42020-04-21 10:57:42 +02006441 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006442 if (cmd == NULL || var == NULL) {
6443 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6444 goto error;
6445 }
6446 var->data.type = SMP_T_STR;
6447 var->data.u.str.area = cmd;
6448 var->data.u.str.data = strlen(cmd);
6449 LIST_INIT(&var->list);
6450 LIST_ADDQ(&rules->preset_vars, &var->list);
6451 cmd = NULL;
6452 var = NULL;
6453
Christopher Faulet61cc8522020-04-20 14:54:42 +02006454 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006455 if (rs)
6456 goto ruleset_found;
6457
Christopher Faulet61cc8522020-04-20 14:54:42 +02006458 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006459 if (rs == NULL) {
6460 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6461 goto error;
6462 }
6463
6464 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6465 1, curpx, &rs->rules, file, line, &errmsg);
6466 if (!chk) {
6467 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6468 goto error;
6469 }
6470 chk->index = 0;
6471 LIST_ADDQ(&rs->rules, &chk->list);
6472
6473 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6474 "min-recv", "4",
6475 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006476 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006477 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006478 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006479 if (!chk) {
6480 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6481 goto error;
6482 }
6483 chk->index = 1;
6484 LIST_ADDQ(&rs->rules, &chk->list);
6485
6486 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6487 "min-recv", "4",
6488 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006489 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6490 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006491 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006492 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006493 if (!chk) {
6494 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6495 goto error;
6496 }
6497 chk->index = 2;
6498 LIST_ADDQ(&rs->rules, &chk->list);
6499
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006500 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006501 1, curpx, &rs->rules, file, line, &errmsg);
6502 if (!chk) {
6503 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6504 goto error;
6505 }
6506 chk->index = 3;
6507 LIST_ADDQ(&rs->rules, &chk->list);
6508
6509 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6510 "min-recv", "4",
6511 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006512 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6513 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6514 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006515 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006516 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006517 if (!chk) {
6518 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6519 goto error;
6520 }
6521 chk->index = 4;
6522 LIST_ADDQ(&rs->rules, &chk->list);
6523
Christopher Fauletd7cee712020-04-21 13:45:00 +02006524 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006525
6526 ruleset_found:
6527 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006528 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006529
6530 out:
6531 free(errmsg);
6532 return err_code;
6533
6534 error:
6535 free(cmd);
6536 free(var);
6537 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006538 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006539 err_code |= ERR_ALERT | ERR_FATAL;
6540 goto out;
6541}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006542
Christopher Fauletce355072020-04-02 11:44:39 +02006543/* Parses the "option pgsql-check" proxy keyword */
6544int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6545 const char *file, int line)
6546{
6547 static char pgsql_req[] = {
6548 "%[var(check.plen),htonl,hex]" /* The packet length*/
6549 "00030000" /* the version 3.0 */
6550 "7573657200" /* "user" key */
6551 "%[var(check.username),hex]00" /* the username */
6552 "00"
6553 };
6554
6555 struct tcpcheck_ruleset *rs = NULL;
6556 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6557 struct tcpcheck_rule *chk;
6558 struct tcpcheck_var *var = NULL;
6559 char *user = NULL, *errmsg = NULL;
6560 size_t packetlen = 0;
6561 int err_code = 0;
6562
6563 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6564 err_code |= ERR_WARN;
6565
6566 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6567 goto out;
6568
Christopher Fauletce355072020-04-02 11:44:39 +02006569 curpx->options2 &= ~PR_O2_CHK_ANY;
6570 curpx->options2 |= PR_O2_TCPCHK_CHK;
6571
6572 free_tcpcheck_vars(&rules->preset_vars);
6573 rules->list = NULL;
6574 rules->flags = 0;
6575
6576 cur_arg += 2;
6577 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6578 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6579 file, line, args[0], args[1]);
6580 goto error;
6581 }
6582 if (strcmp(args[cur_arg], "user") == 0) {
6583 packetlen = 15 + strlen(args[cur_arg+1]);
6584 user = strdup(args[cur_arg+1]);
6585
Christopher Fauletb61caf42020-04-21 10:57:42 +02006586 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006587 if (user == NULL || var == NULL) {
6588 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6589 goto error;
6590 }
6591 var->data.type = SMP_T_STR;
6592 var->data.u.str.area = user;
6593 var->data.u.str.data = strlen(user);
6594 LIST_INIT(&var->list);
6595 LIST_ADDQ(&rules->preset_vars, &var->list);
6596 user = NULL;
6597 var = NULL;
6598
Christopher Fauletb61caf42020-04-21 10:57:42 +02006599 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006600 if (var == NULL) {
6601 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6602 goto error;
6603 }
6604 var->data.type = SMP_T_SINT;
6605 var->data.u.sint = packetlen;
6606 LIST_INIT(&var->list);
6607 LIST_ADDQ(&rules->preset_vars, &var->list);
6608 var = NULL;
6609 }
6610 else {
6611 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6612 file, line, args[0], args[1]);
6613 goto error;
6614 }
6615
Christopher Faulet61cc8522020-04-20 14:54:42 +02006616 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006617 if (rs)
6618 goto ruleset_found;
6619
Christopher Faulet61cc8522020-04-20 14:54:42 +02006620 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006621 if (rs == NULL) {
6622 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6623 goto error;
6624 }
6625
6626 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6627 1, curpx, &rs->rules, file, line, &errmsg);
6628 if (!chk) {
6629 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6630 goto error;
6631 }
6632 chk->index = 0;
6633 LIST_ADDQ(&rs->rules, &chk->list);
6634
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006635 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006636 1, curpx, &rs->rules, file, line, &errmsg);
6637 if (!chk) {
6638 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6639 goto error;
6640 }
6641 chk->index = 1;
6642 LIST_ADDQ(&rs->rules, &chk->list);
6643
6644 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6645 "min-recv", "5",
6646 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006647 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006648 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006649 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006650 if (!chk) {
6651 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6652 goto error;
6653 }
6654 chk->index = 2;
6655 LIST_ADDQ(&rs->rules, &chk->list);
6656
Christopher Fauletb841c742020-04-27 18:29:49 +02006657 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 +02006658 "min-recv", "9",
6659 "error-status", "L7STS",
6660 "on-success", "PostgreSQL server is ok",
6661 "on-error", "PostgreSQL unknown error",
6662 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006663 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006664 if (!chk) {
6665 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6666 goto error;
6667 }
6668 chk->index = 3;
6669 LIST_ADDQ(&rs->rules, &chk->list);
6670
Christopher Fauletd7cee712020-04-21 13:45:00 +02006671 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletce355072020-04-02 11:44:39 +02006672
6673 ruleset_found:
6674 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006675 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006676
6677 out:
6678 free(errmsg);
6679 return err_code;
6680
6681 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006682 free(user);
6683 free(var);
6684 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006685 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006686 err_code |= ERR_ALERT | ERR_FATAL;
6687 goto out;
6688}
6689
6690
6691/* Parses the "option mysql-check" proxy keyword */
6692int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6693 const char *file, int line)
6694{
6695 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6696 * const char mysql40_client_auth_pkt[] = {
6697 * "\x0e\x00\x00" // packet length
6698 * "\x01" // packet number
6699 * "\x00\x00" // client capabilities
6700 * "\x00\x00\x01" // max packet
6701 * "haproxy\x00" // username (null terminated string)
6702 * "\x00" // filler (always 0x00)
6703 * "\x01\x00\x00" // packet length
6704 * "\x00" // packet number
6705 * "\x01" // COM_QUIT command
6706 * };
6707 */
6708 static char mysql40_rsname[] = "*mysql40-check";
6709 static char mysql40_req[] = {
6710 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6711 "0080" /* client capabilities */
6712 "000001" /* max packet */
6713 "%[var(check.username),hex]00" /* the username */
6714 "00" /* filler (always 0x00) */
6715 "010000" /* packet length*/
6716 "00" /* sequence ID */
6717 "01" /* COM_QUIT command */
6718 };
6719
6720 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6721 * const char mysql41_client_auth_pkt[] = {
6722 * "\x0e\x00\x00\" // packet length
6723 * "\x01" // packet number
6724 * "\x00\x00\x00\x00" // client capabilities
6725 * "\x00\x00\x00\x01" // max packet
6726 * "\x21" // character set (UTF-8)
6727 * char[23] // All zeroes
6728 * "haproxy\x00" // username (null terminated string)
6729 * "\x00" // filler (always 0x00)
6730 * "\x01\x00\x00" // packet length
6731 * "\x00" // packet number
6732 * "\x01" // COM_QUIT command
6733 * };
6734 */
6735 static char mysql41_rsname[] = "*mysql41-check";
6736 static char mysql41_req[] = {
6737 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6738 "00820000" /* client capabilities */
6739 "00800001" /* max packet */
6740 "21" /* character set (UTF-8) */
6741 "000000000000000000000000" /* 23 bytes, al zeroes */
6742 "0000000000000000000000"
6743 "%[var(check.username),hex]00" /* the username */
6744 "00" /* filler (always 0x00) */
6745 "010000" /* packet length*/
6746 "00" /* sequence ID */
6747 "01" /* COM_QUIT command */
6748 };
6749
6750 struct tcpcheck_ruleset *rs = NULL;
6751 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6752 struct tcpcheck_rule *chk;
6753 struct tcpcheck_var *var = NULL;
6754 char *mysql_rsname = "*mysql-check";
6755 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6756 int index = 0, err_code = 0;
6757
6758 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6759 err_code |= ERR_WARN;
6760
6761 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6762 goto out;
6763
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006764 curpx->options2 &= ~PR_O2_CHK_ANY;
6765 curpx->options2 |= PR_O2_TCPCHK_CHK;
6766
6767 free_tcpcheck_vars(&rules->preset_vars);
6768 rules->list = NULL;
6769 rules->flags = 0;
6770
6771 cur_arg += 2;
6772 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006773 int packetlen, userlen;
6774
6775 if (strcmp(args[cur_arg], "user") != 0) {
6776 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6777 file, line, args[0], args[1], args[cur_arg]);
6778 goto error;
6779 }
6780
6781 if (*(args[cur_arg+1]) == 0) {
6782 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6783 file, line, args[0], args[1], args[cur_arg]);
6784 goto error;
6785 }
6786
6787 hdr = calloc(4, sizeof(*hdr));
6788 user = strdup(args[cur_arg+1]);
6789 userlen = strlen(args[cur_arg+1]);
6790
6791 if (hdr == NULL || user == NULL) {
6792 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6793 goto error;
6794 }
6795
6796 if (*args[cur_arg+2]) {
6797 if (strcmp(args[cur_arg+2], "post-41") != 0) {
6798 ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41' (got '%s').\n",
6799 file, line, args[cur_arg], args[cur_arg+2]);
6800 goto error;
6801 }
6802 packetlen = userlen + 7 + 27;
6803 mysql_req = mysql41_req;
6804 mysql_rsname = mysql41_rsname;
6805 }
6806 else {
6807 packetlen = userlen + 7;
6808 mysql_req = mysql40_req;
6809 mysql_rsname = mysql40_rsname;
6810 }
6811
6812 hdr[0] = (unsigned char)(packetlen & 0xff);
6813 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6814 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6815 hdr[3] = 1;
6816
Christopher Fauletb61caf42020-04-21 10:57:42 +02006817 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006818 if (var == NULL) {
6819 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6820 goto error;
6821 }
6822 var->data.type = SMP_T_STR;
6823 var->data.u.str.area = hdr;
6824 var->data.u.str.data = 4;
6825 LIST_INIT(&var->list);
6826 LIST_ADDQ(&rules->preset_vars, &var->list);
6827 hdr = NULL;
6828 var = NULL;
6829
Christopher Fauletb61caf42020-04-21 10:57:42 +02006830 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006831 if (var == NULL) {
6832 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6833 goto error;
6834 }
6835 var->data.type = SMP_T_STR;
6836 var->data.u.str.area = user;
6837 var->data.u.str.data = strlen(user);
6838 LIST_INIT(&var->list);
6839 LIST_ADDQ(&rules->preset_vars, &var->list);
6840 user = NULL;
6841 var = NULL;
6842 }
6843
Christopher Faulet61cc8522020-04-20 14:54:42 +02006844 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006845 if (rs)
6846 goto ruleset_found;
6847
Christopher Faulet61cc8522020-04-20 14:54:42 +02006848 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006849 if (rs == NULL) {
6850 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6851 goto error;
6852 }
6853
6854 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6855 1, curpx, &rs->rules, file, line, &errmsg);
6856 if (!chk) {
6857 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6858 goto error;
6859 }
6860 chk->index = index++;
6861 LIST_ADDQ(&rs->rules, &chk->list);
6862
6863 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006864 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006865 1, curpx, &rs->rules, file, line, &errmsg);
6866 if (!chk) {
6867 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6868 goto error;
6869 }
6870 chk->index = index++;
6871 LIST_ADDQ(&rs->rules, &chk->list);
6872 }
6873
6874 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006875 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006876 if (!chk) {
6877 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6878 goto error;
6879 }
6880 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6881 chk->index = index++;
6882 LIST_ADDQ(&rs->rules, &chk->list);
6883
6884 if (mysql_req) {
6885 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006886 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006887 if (!chk) {
6888 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6889 goto error;
6890 }
6891 chk->expect.custom = tcpcheck_mysql_expect_ok;
6892 chk->index = index++;
6893 LIST_ADDQ(&rs->rules, &chk->list);
6894 }
6895
Christopher Fauletd7cee712020-04-21 13:45:00 +02006896 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006897
6898 ruleset_found:
6899 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006900 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006901
6902 out:
6903 free(errmsg);
6904 return err_code;
6905
6906 error:
6907 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006908 free(user);
6909 free(var);
6910 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006911 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006912 err_code |= ERR_ALERT | ERR_FATAL;
6913 goto out;
6914}
6915
Christopher Faulet1997eca2020-04-03 23:13:50 +02006916int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6917 const char *file, int line)
6918{
6919 static char *ldap_req = "300C020101600702010304008000";
6920
6921 struct tcpcheck_ruleset *rs = NULL;
6922 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6923 struct tcpcheck_rule *chk;
6924 char *errmsg = NULL;
6925 int err_code = 0;
6926
6927 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6928 err_code |= ERR_WARN;
6929
6930 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6931 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006932
6933 curpx->options2 &= ~PR_O2_CHK_ANY;
6934 curpx->options2 |= PR_O2_TCPCHK_CHK;
6935
6936 free_tcpcheck_vars(&rules->preset_vars);
6937 rules->list = NULL;
6938 rules->flags = 0;
6939
Christopher Faulet61cc8522020-04-20 14:54:42 +02006940 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006941 if (rs)
6942 goto ruleset_found;
6943
Christopher Faulet61cc8522020-04-20 14:54:42 +02006944 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006945 if (rs == NULL) {
6946 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6947 goto error;
6948 }
6949
6950 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6951 1, curpx, &rs->rules, file, line, &errmsg);
6952 if (!chk) {
6953 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6954 goto error;
6955 }
6956 chk->index = 0;
6957 LIST_ADDQ(&rs->rules, &chk->list);
6958
6959 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6960 "min-recv", "14",
6961 "on-error", "Not LDAPv3 protocol",
6962 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006963 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006964 if (!chk) {
6965 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6966 goto error;
6967 }
6968 chk->index = 1;
6969 LIST_ADDQ(&rs->rules, &chk->list);
6970
6971 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006972 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006973 if (!chk) {
6974 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6975 goto error;
6976 }
6977 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6978 chk->index = 2;
6979 LIST_ADDQ(&rs->rules, &chk->list);
6980
Christopher Fauletd7cee712020-04-21 13:45:00 +02006981 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006982
6983 ruleset_found:
6984 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006985 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006986
6987 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006988 free(errmsg);
6989 return err_code;
6990
6991 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006992 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006993 err_code |= ERR_ALERT | ERR_FATAL;
6994 goto out;
6995}
6996
6997int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6998 const char *file, int line)
6999{
7000 struct tcpcheck_ruleset *rs = NULL;
7001 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7002 struct tcpcheck_rule *chk;
7003 char *spop_req = NULL;
7004 char *errmsg = NULL;
7005 int spop_len = 0, err_code = 0;
7006
7007 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7008 err_code |= ERR_WARN;
7009
7010 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7011 goto out;
7012
Christopher Faulet267b01b2020-04-04 10:27:09 +02007013 curpx->options2 &= ~PR_O2_CHK_ANY;
7014 curpx->options2 |= PR_O2_TCPCHK_CHK;
7015
7016 free_tcpcheck_vars(&rules->preset_vars);
7017 rules->list = NULL;
7018 rules->flags = 0;
7019
7020
Christopher Faulet61cc8522020-04-20 14:54:42 +02007021 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007022 if (rs)
7023 goto ruleset_found;
7024
Christopher Faulet61cc8522020-04-20 14:54:42 +02007025 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007026 if (rs == NULL) {
7027 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7028 goto error;
7029 }
7030
7031 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7032 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7033 goto error;
7034 }
7035 chunk_reset(&trash);
7036 dump_binary(&trash, spop_req, spop_len);
7037 trash.area[trash.data] = '\0';
7038
7039 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7040 1, curpx, &rs->rules, file, line, &errmsg);
7041 if (!chk) {
7042 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7043 goto error;
7044 }
7045 chk->index = 0;
7046 LIST_ADDQ(&rs->rules, &chk->list);
7047
7048 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007049 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007050 if (!chk) {
7051 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7052 goto error;
7053 }
7054 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7055 chk->index = 1;
7056 LIST_ADDQ(&rs->rules, &chk->list);
7057
Christopher Fauletd7cee712020-04-21 13:45:00 +02007058 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007059
7060 ruleset_found:
7061 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007062 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007063
7064 out:
7065 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007066 free(errmsg);
7067 return err_code;
7068
7069 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007070 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007071 err_code |= ERR_ALERT | ERR_FATAL;
7072 goto out;
7073}
Christopher Fauletce355072020-04-02 11:44:39 +02007074
Christopher Faulete5870d82020-04-15 11:32:03 +02007075
7076struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7077{
7078 struct tcpcheck_rule *chk = NULL;
7079 struct tcpcheck_http_hdr *hdr = NULL;
7080 char *meth = NULL, *uri = NULL, *vsn = NULL;
7081 char *hdrs, *body;
7082
7083 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7084 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7085 if (hdrs == body)
7086 hdrs = NULL;
7087 if (hdrs) {
7088 *hdrs = '\0';
7089 hdrs +=2;
7090 }
7091 if (body) {
7092 *body = '\0';
7093 body += 4;
7094 }
7095 if (hdrs || body) {
7096 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7097 " Please, consider to use 'http-check send' directive instead.");
7098 }
7099
7100 chk = calloc(1, sizeof(*chk));
7101 if (!chk) {
7102 memprintf(errmsg, "out of memory");
7103 goto error;
7104 }
7105 chk->action = TCPCHK_ACT_SEND;
7106 chk->send.type = TCPCHK_SEND_HTTP;
7107 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7108 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7109 LIST_INIT(&chk->send.http.hdrs);
7110
7111 /* Copy the method, uri and version */
7112 if (*args[cur_arg]) {
7113 if (!*args[cur_arg+1])
7114 uri = args[cur_arg];
7115 else
7116 meth = args[cur_arg];
7117 }
7118 if (*args[cur_arg+1])
7119 uri = args[cur_arg+1];
7120 if (*args[cur_arg+2])
7121 vsn = args[cur_arg+2];
7122
7123 if (meth) {
7124 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7125 chk->send.http.meth.str.area = strdup(meth);
7126 chk->send.http.meth.str.data = strlen(meth);
7127 if (!chk->send.http.meth.str.area) {
7128 memprintf(errmsg, "out of memory");
7129 goto error;
7130 }
7131 }
7132 if (uri) {
7133 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007134 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007135 memprintf(errmsg, "out of memory");
7136 goto error;
7137 }
7138 }
7139 if (vsn) {
7140 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007141 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007142 memprintf(errmsg, "out of memory");
7143 goto error;
7144 }
7145 }
7146
7147 /* Copy the header */
7148 if (hdrs) {
7149 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7150 struct h1m h1m;
7151 int i, ret;
7152
7153 /* Build and parse the request */
7154 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7155
7156 h1m.flags = H1_MF_HDRS_ONLY;
7157 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7158 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7159 &h1m, NULL);
7160 if (ret <= 0) {
7161 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7162 goto error;
7163 }
7164
Christopher Fauletb61caf42020-04-21 10:57:42 +02007165 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007166 hdr = calloc(1, sizeof(*hdr));
7167 if (!hdr) {
7168 memprintf(errmsg, "out of memory");
7169 goto error;
7170 }
7171 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007172 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007173 if (!hdr->name.ptr) {
7174 memprintf(errmsg, "out of memory");
7175 goto error;
7176 }
7177
Christopher Fauletb61caf42020-04-21 10:57:42 +02007178 ist0(tmp_hdrs[i].v);
7179 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 +02007180 goto error;
7181 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7182 }
7183 }
7184
7185 /* Copy the body */
7186 if (body) {
7187 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007188 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007189 memprintf(errmsg, "out of memory");
7190 goto error;
7191 }
7192 }
7193
7194 return chk;
7195
7196 error:
7197 free_tcpcheck_http_hdr(hdr);
7198 free_tcpcheck(chk, 0);
7199 return NULL;
7200}
7201
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007202int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7203 const char *file, int line)
7204{
Christopher Faulete5870d82020-04-15 11:32:03 +02007205 struct tcpcheck_ruleset *rs = NULL;
7206 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7207 struct tcpcheck_rule *chk;
7208 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007209 int err_code = 0;
7210
7211 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7212 err_code |= ERR_WARN;
7213
7214 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7215 goto out;
7216
Christopher Faulete5870d82020-04-15 11:32:03 +02007217 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7218 if (!chk) {
7219 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7220 goto error;
7221 }
7222 if (errmsg) {
7223 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7224 err_code |= ERR_WARN;
7225 free(errmsg);
7226 errmsg = NULL;
7227 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007228
Christopher Faulete5870d82020-04-15 11:32:03 +02007229 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007230 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007231 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007232
Christopher Faulete5870d82020-04-15 11:32:03 +02007233 free_tcpcheck_vars(&rules->preset_vars);
7234 rules->list = NULL;
7235 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007236
Christopher Faulete5870d82020-04-15 11:32:03 +02007237 /* Deduce the ruleset name from the proxy info */
7238 chunk_printf(&trash, "*http-check-%s_%s-%d",
7239 ((curpx == defpx) ? "defaults" : curpx->id),
7240 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007241
Christopher Faulet61cc8522020-04-20 14:54:42 +02007242 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007243 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007244 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007245 if (rs == NULL) {
7246 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7247 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007248 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007249 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007250
Christopher Faulete5870d82020-04-15 11:32:03 +02007251 rules->list = &rs->rules;
7252 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7253 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7254 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7255 rules->list = NULL;
7256 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007257 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007258
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007259 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007260 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007261 return err_code;
7262
7263 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007264 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007265 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007266 err_code |= ERR_ALERT | ERR_FATAL;
7267 goto out;
7268}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007269
Christopher Faulet6f557912020-04-09 15:58:50 +02007270int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7271 const char *file, int line)
7272{
7273 int err_code = 0;
7274
Christopher Faulet6f557912020-04-09 15:58:50 +02007275 curpx->options2 &= ~PR_O2_CHK_ANY;
7276 curpx->options2 |= PR_O2_EXT_CHK;
7277 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7278 goto out;
7279
7280 out:
7281 return err_code;
7282}
7283
Christopher Fauletce8111e2020-04-06 15:04:11 +02007284/* Parse the "addr" server keyword */
7285static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7286 char **errmsg)
7287{
7288 struct sockaddr_storage *sk;
7289 struct protocol *proto;
7290 int port1, port2, err_code = 0;
7291
7292
7293 if (!*args[*cur_arg+1]) {
7294 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7295 goto error;
7296 }
7297
7298 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7299 if (!sk) {
7300 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7301 goto error;
7302 }
7303
7304 proto = protocol_by_family(sk->ss_family);
7305 if (!proto || !proto->connect) {
7306 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7307 args[*cur_arg], args[*cur_arg+1]);
7308 goto error;
7309 }
7310
7311 if (port1 != port2) {
7312 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7313 args[*cur_arg], args[*cur_arg+1]);
7314 goto error;
7315 }
7316
7317 srv->check.addr = srv->agent.addr = *sk;
7318 srv->flags |= SRV_F_CHECKADDR;
7319 srv->flags |= SRV_F_AGENTADDR;
7320
7321 out:
7322 return err_code;
7323
7324 error:
7325 err_code |= ERR_ALERT | ERR_FATAL;
7326 goto out;
7327}
7328
7329
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007330/* Parse the "agent-addr" server keyword */
7331static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7332 char **errmsg)
7333{
7334 int err_code = 0;
7335
7336 if (!*(args[*cur_arg+1])) {
7337 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7338 goto error;
7339 }
7340 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7341 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7342 goto error;
7343 }
7344
7345 out:
7346 return err_code;
7347
7348 error:
7349 err_code |= ERR_ALERT | ERR_FATAL;
7350 goto out;
7351}
7352
7353/* Parse the "agent-check" server keyword */
7354static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7355 char **errmsg)
7356{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007357 struct tcpcheck_ruleset *rs = NULL;
7358 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7359 struct tcpcheck_rule *chk;
7360 int err_code = 0;
7361
7362 if (srv->do_agent)
7363 goto out;
7364
7365 if (!rules) {
7366 rules = calloc(1, sizeof(*rules));
7367 if (!rules) {
7368 memprintf(errmsg, "out of memory.");
7369 goto error;
7370 }
7371 LIST_INIT(&rules->preset_vars);
7372 srv->agent.tcpcheck_rules = rules;
7373 }
7374 rules->list = NULL;
7375 rules->flags = 0;
7376
Christopher Faulet61cc8522020-04-20 14:54:42 +02007377 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007378 if (rs)
7379 goto ruleset_found;
7380
Christopher Faulet61cc8522020-04-20 14:54:42 +02007381 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007382 if (rs == NULL) {
7383 memprintf(errmsg, "out of memory.");
7384 goto error;
7385 }
7386
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007387 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007388 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7389 if (!chk) {
7390 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7391 goto error;
7392 }
7393 chk->index = 0;
7394 LIST_ADDQ(&rs->rules, &chk->list);
7395
7396 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007397 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7398 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007399 if (!chk) {
7400 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7401 goto error;
7402 }
7403 chk->expect.custom = tcpcheck_agent_expect_reply;
7404 chk->index = 1;
7405 LIST_ADDQ(&rs->rules, &chk->list);
7406
Christopher Fauletd7cee712020-04-21 13:45:00 +02007407 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007408
7409 ruleset_found:
7410 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007411 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007412 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007413
7414 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007415 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007416
7417 error:
7418 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007419 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007420 err_code |= ERR_ALERT | ERR_FATAL;
7421 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007422}
7423
7424/* Parse the "agent-inter" server keyword */
7425static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7426 char **errmsg)
7427{
7428 const char *err = NULL;
7429 unsigned int delay;
7430 int err_code = 0;
7431
7432 if (!*(args[*cur_arg+1])) {
7433 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7434 goto error;
7435 }
7436
7437 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7438 if (err == PARSE_TIME_OVER) {
7439 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7440 args[*cur_arg+1], args[*cur_arg], srv->id);
7441 goto error;
7442 }
7443 else if (err == PARSE_TIME_UNDER) {
7444 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7445 args[*cur_arg+1], args[*cur_arg], srv->id);
7446 goto error;
7447 }
7448 else if (err) {
7449 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7450 *err, srv->id);
7451 goto error;
7452 }
7453 if (delay <= 0) {
7454 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7455 delay, args[*cur_arg], srv->id);
7456 goto error;
7457 }
7458 srv->agent.inter = delay;
7459
7460 out:
7461 return err_code;
7462
7463 error:
7464 err_code |= ERR_ALERT | ERR_FATAL;
7465 goto out;
7466}
7467
7468/* Parse the "agent-port" server keyword */
7469static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7470 char **errmsg)
7471{
7472 int err_code = 0;
7473
7474 if (!*(args[*cur_arg+1])) {
7475 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7476 goto error;
7477 }
7478
7479 global.maxsock++;
7480 srv->agent.port = atol(args[*cur_arg+1]);
7481
7482 out:
7483 return err_code;
7484
7485 error:
7486 err_code |= ERR_ALERT | ERR_FATAL;
7487 goto out;
7488}
7489
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007490int set_srv_agent_send(struct server *srv, const char *send)
7491{
7492 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7493 struct tcpcheck_var *var = NULL;
7494 char *str;
7495
7496 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007497 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007498 if (str == NULL || var == NULL)
7499 goto error;
7500
7501 free_tcpcheck_vars(&rules->preset_vars);
7502
7503 var->data.type = SMP_T_STR;
7504 var->data.u.str.area = str;
7505 var->data.u.str.data = strlen(str);
7506 LIST_INIT(&var->list);
7507 LIST_ADDQ(&rules->preset_vars, &var->list);
7508
7509 return 1;
7510
7511 error:
7512 free(str);
7513 free(var);
7514 return 0;
7515}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007516
7517/* Parse the "agent-send" server keyword */
7518static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7519 char **errmsg)
7520{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007521 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007522 int err_code = 0;
7523
7524 if (!*(args[*cur_arg+1])) {
7525 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7526 goto error;
7527 }
7528
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007529 if (!rules) {
7530 rules = calloc(1, sizeof(*rules));
7531 if (!rules) {
7532 memprintf(errmsg, "out of memory.");
7533 goto error;
7534 }
7535 LIST_INIT(&rules->preset_vars);
7536 srv->agent.tcpcheck_rules = rules;
7537 }
7538
7539 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007540 memprintf(errmsg, "out of memory.");
7541 goto error;
7542 }
7543
7544 out:
7545 return err_code;
7546
7547 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007548 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007549 err_code |= ERR_ALERT | ERR_FATAL;
7550 goto out;
7551}
7552
7553/* Parse the "no-agent-send" server keyword */
7554static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7555 char **errmsg)
7556{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007557 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007558 return 0;
7559}
7560
Christopher Fauletce8111e2020-04-06 15:04:11 +02007561/* Parse the "check" server keyword */
7562static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7563 char **errmsg)
7564{
7565 srv->do_check = 1;
7566 return 0;
7567}
7568
7569/* Parse the "check-send-proxy" server keyword */
7570static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7571 char **errmsg)
7572{
7573 srv->check.send_proxy = 1;
7574 return 0;
7575}
7576
7577/* Parse the "check-via-socks4" server keyword */
7578static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7579 char **errmsg)
7580{
7581 srv->check.via_socks4 = 1;
7582 return 0;
7583}
7584
7585/* Parse the "no-check" server keyword */
7586static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7587 char **errmsg)
7588{
7589 deinit_srv_check(srv);
7590 return 0;
7591}
7592
7593/* Parse the "no-check-send-proxy" server keyword */
7594static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7595 char **errmsg)
7596{
7597 srv->check.send_proxy = 0;
7598 return 0;
7599}
7600
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007601/* parse the "check-proto" server keyword */
7602static int srv_parse_check_proto(char **args, int *cur_arg,
7603 struct proxy *px, struct server *newsrv, char **err)
7604{
7605 int err_code = 0;
7606
7607 if (!*args[*cur_arg + 1]) {
7608 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7609 goto error;
7610 }
7611 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7612 if (!newsrv->check.mux_proto) {
7613 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7614 goto error;
7615 }
7616
7617 out:
7618 return err_code;
7619
7620 error:
7621 err_code |= ERR_ALERT | ERR_FATAL;
7622 goto out;
7623}
7624
7625
Christopher Fauletce8111e2020-04-06 15:04:11 +02007626/* Parse the "rise" server keyword */
7627static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7628 char **errmsg)
7629{
7630 int err_code = 0;
7631
7632 if (!*args[*cur_arg + 1]) {
7633 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7634 goto error;
7635 }
7636
7637 srv->check.rise = atol(args[*cur_arg+1]);
7638 if (srv->check.rise <= 0) {
7639 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7640 goto error;
7641 }
7642
7643 if (srv->check.health)
7644 srv->check.health = srv->check.rise;
7645
7646 out:
7647 return err_code;
7648
7649 error:
7650 deinit_srv_agent_check(srv);
7651 err_code |= ERR_ALERT | ERR_FATAL;
7652 goto out;
7653 return 0;
7654}
7655
7656/* Parse the "fall" server keyword */
7657static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7658 char **errmsg)
7659{
7660 int err_code = 0;
7661
7662 if (!*args[*cur_arg + 1]) {
7663 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7664 goto error;
7665 }
7666
7667 srv->check.fall = atol(args[*cur_arg+1]);
7668 if (srv->check.fall <= 0) {
7669 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7670 goto error;
7671 }
7672
7673 out:
7674 return err_code;
7675
7676 error:
7677 deinit_srv_agent_check(srv);
7678 err_code |= ERR_ALERT | ERR_FATAL;
7679 goto out;
7680 return 0;
7681}
7682
7683/* Parse the "inter" server keyword */
7684static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7685 char **errmsg)
7686{
7687 const char *err = NULL;
7688 unsigned int delay;
7689 int err_code = 0;
7690
7691 if (!*(args[*cur_arg+1])) {
7692 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7693 goto error;
7694 }
7695
7696 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7697 if (err == PARSE_TIME_OVER) {
7698 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7699 args[*cur_arg+1], args[*cur_arg], srv->id);
7700 goto error;
7701 }
7702 else if (err == PARSE_TIME_UNDER) {
7703 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7704 args[*cur_arg+1], args[*cur_arg], srv->id);
7705 goto error;
7706 }
7707 else if (err) {
7708 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7709 *err, srv->id);
7710 goto error;
7711 }
7712 if (delay <= 0) {
7713 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7714 delay, args[*cur_arg], srv->id);
7715 goto error;
7716 }
7717 srv->check.inter = delay;
7718
7719 out:
7720 return err_code;
7721
7722 error:
7723 err_code |= ERR_ALERT | ERR_FATAL;
7724 goto out;
7725}
7726
7727
7728/* Parse the "fastinter" server keyword */
7729static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7730 char **errmsg)
7731{
7732 const char *err = NULL;
7733 unsigned int delay;
7734 int err_code = 0;
7735
7736 if (!*(args[*cur_arg+1])) {
7737 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7738 goto error;
7739 }
7740
7741 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7742 if (err == PARSE_TIME_OVER) {
7743 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7744 args[*cur_arg+1], args[*cur_arg], srv->id);
7745 goto error;
7746 }
7747 else if (err == PARSE_TIME_UNDER) {
7748 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7749 args[*cur_arg+1], args[*cur_arg], srv->id);
7750 goto error;
7751 }
7752 else if (err) {
7753 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7754 *err, srv->id);
7755 goto error;
7756 }
7757 if (delay <= 0) {
7758 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7759 delay, args[*cur_arg], srv->id);
7760 goto error;
7761 }
7762 srv->check.fastinter = delay;
7763
7764 out:
7765 return err_code;
7766
7767 error:
7768 err_code |= ERR_ALERT | ERR_FATAL;
7769 goto out;
7770}
7771
7772
7773/* Parse the "downinter" server keyword */
7774static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7775 char **errmsg)
7776{
7777 const char *err = NULL;
7778 unsigned int delay;
7779 int err_code = 0;
7780
7781 if (!*(args[*cur_arg+1])) {
7782 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7783 goto error;
7784 }
7785
7786 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7787 if (err == PARSE_TIME_OVER) {
7788 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7789 args[*cur_arg+1], args[*cur_arg], srv->id);
7790 goto error;
7791 }
7792 else if (err == PARSE_TIME_UNDER) {
7793 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7794 args[*cur_arg+1], args[*cur_arg], srv->id);
7795 goto error;
7796 }
7797 else if (err) {
7798 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7799 *err, srv->id);
7800 goto error;
7801 }
7802 if (delay <= 0) {
7803 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7804 delay, args[*cur_arg], srv->id);
7805 goto error;
7806 }
7807 srv->check.downinter = delay;
7808
7809 out:
7810 return err_code;
7811
7812 error:
7813 err_code |= ERR_ALERT | ERR_FATAL;
7814 goto out;
7815}
7816
7817/* Parse the "port" server keyword */
7818static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7819 char **errmsg)
7820{
7821 int err_code = 0;
7822
7823 if (!*(args[*cur_arg+1])) {
7824 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7825 goto error;
7826 }
7827
7828 global.maxsock++;
7829 srv->check.port = atol(args[*cur_arg+1]);
7830 srv->flags |= SRV_F_CHECKPORT;
7831
7832 out:
7833 return err_code;
7834
7835 error:
7836 err_code |= ERR_ALERT | ERR_FATAL;
7837 goto out;
7838}
7839
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007840static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007841 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7842 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7843 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007844 { 0, NULL, NULL },
7845}};
7846
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007847static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007848 { "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 +02007849 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7850 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7851 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7852 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7853 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007854 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007855 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007856 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7857 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007858 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007859 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7860 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7861 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7862 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7863 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7864 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7865 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7866 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007867 { NULL, NULL, 0 },
7868}};
7869
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007870INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007871INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007872
Willy Tarreaubd741542010-03-16 18:46:54 +01007873/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007874 * Local variables:
7875 * c-indent-level: 8
7876 * c-basic-offset: 8
7877 * End:
7878 */