blob: d22c113100badd259a2008452428d561f425649e [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
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001064 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001065 * 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
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001152 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001153 * 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;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001682 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001683 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001684
Christopher Faulet61cc8522020-04-20 14:54:42 +02001685 /* For a connect action we'll create a new connection. We may also have
1686 * to kill a previous one. But we don't want to leave *without* a
1687 * connection if we came here from the connection layer, hence with a
1688 * connection. Thus we'll proceed in the following order :
1689 * 1: close but not release previous connection (handled by the caller)
1690 * 2: try to get a new connection
1691 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001692 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001693
Christopher Faulet61cc8522020-04-20 14:54:42 +02001694 /* 2- prepare new connection */
1695 cs = cs_new(NULL);
1696 if (!cs) {
1697 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1698 tcpcheck_get_step_id(check, rule));
1699 if (rule->comment)
1700 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1701 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1702 ret = TCPCHK_EVAL_STOP;
1703 goto out;
1704 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001705
Christopher Faulet61cc8522020-04-20 14:54:42 +02001706 /* 3- release and replace the old one on success */
1707 if (check->cs) {
1708 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001709 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1710 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001711
1712 /* We may have been scheduled to run, and the I/O handler
1713 * expects to have a cs, so remove the tasklet
1714 */
1715 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1716 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001717 }
1718
Christopher Faulet61cc8522020-04-20 14:54:42 +02001719 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001720
Christopher Faulet61cc8522020-04-20 14:54:42 +02001721 check->cs = cs;
1722 conn = cs->conn;
1723 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001724
Christopher Faulet61cc8522020-04-20 14:54:42 +02001725 /* Maybe there were an older connection we were waiting on */
1726 check->wait_list.events = 0;
1727 conn->target = s ? &s->obj_type : &proxy->obj_type;
1728
1729 /* no client address */
1730 if (!sockaddr_alloc(&conn->dst)) {
1731 status = SF_ERR_RESOURCE;
1732 goto fail_check;
1733 }
1734
1735 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001736 * addr if specified on the server. otherwise, use the server addr (it
1737 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001738 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001739 *conn->dst = (is_addr(&connect->addr)
1740 ? connect->addr
1741 : (is_addr(&check->addr) ? check->addr : s->addr));
1742 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001743
Christopher Faulet61cc8522020-04-20 14:54:42 +02001744 port = 0;
1745 if (!port && connect->port)
1746 port = connect->port;
1747 if (!port && connect->port_expr) {
1748 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001749
Christopher Faulet61cc8522020-04-20 14:54:42 +02001750 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1751 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1752 connect->port_expr, SMP_T_SINT);
1753 if (smp)
1754 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001755 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001756 if (!port && is_inet_addr(&connect->addr))
1757 port = get_host_port(&connect->addr);
1758 if (!port && check->port)
1759 port = check->port;
1760 if (!port && is_inet_addr(&check->addr))
1761 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001762 if (!port) {
1763 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001764 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001765 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001766 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001767
Christopher Faulet61cc8522020-04-20 14:54:42 +02001768 xprt = ((connect->options & TCPCHK_OPT_SSL)
1769 ? xprt_get(XPRT_SSL)
1770 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001771
Christopher Faulet61cc8522020-04-20 14:54:42 +02001772 conn_prepare(conn, proto, xprt);
1773 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001774
Christopher Faulet61cc8522020-04-20 14:54:42 +02001775 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001776 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001777 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001778 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001779
Christopher Faulet61cc8522020-04-20 14:54:42 +02001780 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1781 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 if (!next || next->action != TCPCHK_ACT_EXPECT)
1783 flags |= CONNECT_DELACK_ALWAYS;
1784 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001785 }
1786
Christopher Faulet61cc8522020-04-20 14:54:42 +02001787 if (status != SF_ERR_NONE)
1788 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001789
Christopher Faulet61cc8522020-04-20 14:54:42 +02001790 conn->flags |= CO_FL_PRIVATE;
1791 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001792
Christopher Faulet61cc8522020-04-20 14:54:42 +02001793 /* The mux may be initialized now if there isn't server attached to the
1794 * check (email alerts) or if there is a mux proto specified or if there
1795 * is no alpn.
1796 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001797 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1798 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001799 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001800
Christopher Faulet61cc8522020-04-20 14:54:42 +02001801 if (connect->mux_proto)
1802 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001803 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001804 mux_ops = check->mux_proto->mux;
1805 else {
1806 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1807 ? PROTO_MODE_HTTP
1808 : PROTO_MODE_TCP);
1809
1810 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001811 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001812 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1813 status = SF_ERR_INTERNAL;
1814 goto fail_check;
1815 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001816 }
1817
Christopher Faulet61cc8522020-04-20 14:54:42 +02001818#ifdef USE_OPENSSL
1819 if (connect->sni)
1820 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001821 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001822 ssl_sock_set_servername(conn, s->check.sni);
1823
1824 if (connect->alpn)
1825 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001826 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001827 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1828#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001829 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001830 conn->send_proxy_ofs = 1;
1831 conn->flags |= CO_FL_SOCKS4;
1832 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001833 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 +02001834 conn->send_proxy_ofs = 1;
1835 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001836 }
1837
Christopher Faulet61cc8522020-04-20 14:54:42 +02001838 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1839 conn->send_proxy_ofs = 1;
1840 conn->flags |= CO_FL_SEND_PROXY;
1841 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001842 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 +02001843 conn->send_proxy_ofs = 1;
1844 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001845 }
1846
Christopher Faulet61cc8522020-04-20 14:54:42 +02001847 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1848 /* Some servers don't like reset on close */
1849 fdtab[cs->conn->handle.fd].linger_risk = 0;
1850 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001851
Christopher Faulet61cc8522020-04-20 14:54:42 +02001852 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1853 if (xprt_add_hs(conn) < 0)
1854 status = SF_ERR_RESOURCE;
1855 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001856
Christopher Faulet61cc8522020-04-20 14:54:42 +02001857 fail_check:
1858 /* It can return one of :
1859 * - SF_ERR_NONE if everything's OK
1860 * - SF_ERR_SRVTO if there are no more servers
1861 * - SF_ERR_SRVCL if the connection was refused by the server
1862 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1863 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1864 * - SF_ERR_INTERNAL for any other purely internal errors
1865 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1866 * Note that we try to prevent the network stack from sending the ACK during the
1867 * connect() when a pure TCP check is used (without PROXY protocol).
1868 */
1869 switch (status) {
1870 case SF_ERR_NONE:
1871 /* we allow up to min(inter, timeout.connect) for a connection
1872 * to establish but only when timeout.check is set as it may be
1873 * to short for a full check otherwise
1874 */
1875 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001876
Christopher Faulet61cc8522020-04-20 14:54:42 +02001877 if (proxy->timeout.check && proxy->timeout.connect) {
1878 int t_con = tick_add(now_ms, proxy->timeout.connect);
1879 t->expire = tick_first(t->expire, t_con);
1880 }
1881 break;
1882 case SF_ERR_SRVTO: /* ETIMEDOUT */
1883 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1884 case SF_ERR_PRXCOND:
1885 case SF_ERR_RESOURCE:
1886 case SF_ERR_INTERNAL:
1887 chk_report_conn_err(check, errno, 0);
1888 ret = TCPCHK_EVAL_STOP;
1889 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001890 }
1891
Christopher Faulet61cc8522020-04-20 14:54:42 +02001892 /* don't do anything until the connection is established */
1893 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001894 if (conn->mux) {
1895 if (next && next->action == TCPCHK_ACT_SEND)
1896 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1897 else
1898 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1899 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001900 ret = TCPCHK_EVAL_WAIT;
1901 goto out;
1902 }
1903
1904 out:
1905 if (conn && check->result == CHK_RES_FAILED)
1906 conn->flags |= CO_FL_ERROR;
1907 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001908}
1909
Christopher Faulet61cc8522020-04-20 14:54:42 +02001910/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1911 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1912 * TCPCHK_EVAL_STOP if an error occurred.
1913 */
1914static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001915{
1916 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001917 struct tcpcheck_send *send = &rule->send;
1918 struct conn_stream *cs = check->cs;
1919 struct connection *conn = cs_conn(cs);
1920 struct buffer *tmp = NULL;
1921 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001922
Christopher Faulet61cc8522020-04-20 14:54:42 +02001923 /* reset the read & write buffer */
1924 b_reset(&check->bi);
1925 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001926
Christopher Faulet61cc8522020-04-20 14:54:42 +02001927 switch (send->type) {
1928 case TCPCHK_SEND_STRING:
1929 case TCPCHK_SEND_BINARY:
1930 if (istlen(send->data) >= b_size(&check->bo)) {
1931 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1932 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1933 tcpcheck_get_step_id(check, rule));
1934 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1935 ret = TCPCHK_EVAL_STOP;
1936 goto out;
1937 }
1938 b_putist(&check->bo, send->data);
1939 break;
1940 case TCPCHK_SEND_STRING_LF:
1941 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1942 if (!b_data(&check->bo))
1943 goto out;
1944 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001945 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001946 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001947
Christopher Faulet61cc8522020-04-20 14:54:42 +02001948 tmp = alloc_trash_chunk();
1949 if (!tmp)
1950 goto error_lf;
1951 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1952 if (!b_data(tmp))
1953 goto out;
1954 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001955 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001956 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001957 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001958 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001959 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001960 case TCPCHK_SEND_HTTP: {
1961 struct htx_sl *sl;
1962 struct ist meth, uri, vsn, clen, body;
1963 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001964
Christopher Faulet61cc8522020-04-20 14:54:42 +02001965 tmp = alloc_trash_chunk();
1966 if (!tmp)
1967 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001968
Christopher Faulet61cc8522020-04-20 14:54:42 +02001969 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1970 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1971 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001972 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1973 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1974 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1975 }
1976 else
1977 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001978 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001979
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001980 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1981 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001982 slflags |= HTX_SL_F_VER_11;
1983 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1984 if (!isttest(send->http.body))
1985 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001986
Christopher Faulet61cc8522020-04-20 14:54:42 +02001987 htx = htx_from_buf(&check->bo);
1988 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1989 if (!sl)
1990 goto error_htx;
1991 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001992 if (!http_update_host(htx, sl, uri))
1993 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001994
Christopher Faulet61cc8522020-04-20 14:54:42 +02001995 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1996 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001997 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001998
Christopher Faulet61cc8522020-04-20 14:54:42 +02001999 list_for_each_entry(hdr, &send->http.hdrs, list) {
2000 chunk_reset(tmp);
2001 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2002 if (!b_data(tmp))
2003 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002004 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2005 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002006 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002007 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2008 if (!http_update_authority(htx, sl, hdr_value))
2009 goto error_htx;
2010 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002011 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002012
Christopher Faulet61cc8522020-04-20 14:54:42 +02002013 }
2014 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2015 chunk_reset(tmp);
2016 httpchk_build_status_header(check->server, tmp);
2017 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2018 goto error_htx;
2019 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002020
2021
2022 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2023 chunk_reset(tmp);
2024 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2025 body = ist2(b_orig(tmp), b_data(tmp));
2026 }
2027 else
2028 body = send->http.body;
2029 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2030
2031 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2032 !htx_add_header(htx, ist("Content-length"), clen))
2033 goto error_htx;
2034
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002035
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002037 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002038 !htx_add_endof(htx, HTX_BLK_EOM))
2039 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002040
Christopher Faulet61cc8522020-04-20 14:54:42 +02002041 htx_to_buf(htx, &check->bo);
2042 break;
2043 }
2044 case TCPCHK_SEND_UNDEF:
2045 /* Should never happen. */
2046 ret = TCPCHK_EVAL_STOP;
2047 goto out;
2048 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002049
Christopher Faulet6d471212020-04-22 11:09:25 +02002050
2051 if (conn->mux->snd_buf(cs, &check->bo,
2052 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002053 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002054 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002055 goto out;
2056 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002057 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002058 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002059 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2060 ret = TCPCHK_EVAL_WAIT;
2061 goto out;
2062 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002063
Christopher Faulet61cc8522020-04-20 14:54:42 +02002064 out:
2065 free_trash_chunk(tmp);
2066 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002067
Christopher Faulet61cc8522020-04-20 14:54:42 +02002068 error_htx:
2069 if (htx) {
2070 htx_reset(htx);
2071 htx_to_buf(htx, &check->bo);
2072 }
2073 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2074 tcpcheck_get_step_id(check, rule));
2075 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2076 ret = TCPCHK_EVAL_STOP;
2077 goto out;
2078
2079 error_lf:
2080 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2081 tcpcheck_get_step_id(check, rule));
2082 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2083 ret = TCPCHK_EVAL_STOP;
2084 goto out;
2085
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002086}
2087
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002088/* Try to receive data before evaluating a tcp-check expect rule. Returns
2089 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002090 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2091 * TCPCHK_EVAL_STOP if an error occurred.
2092 */
2093static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002094{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002095 struct conn_stream *cs = check->cs;
2096 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002097 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002098 size_t max, read, cur_read = 0;
2099 int is_empty;
2100 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002101
Christopher Faulet61cc8522020-04-20 14:54:42 +02002102 if (check->wait_list.events & SUB_RETRY_RECV)
2103 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002104
Christopher Faulet61cc8522020-04-20 14:54:42 +02002105 if (cs->flags & CS_FL_EOS)
2106 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002107
Christopher Faulet61cc8522020-04-20 14:54:42 +02002108 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002109
Christopher Faulet61cc8522020-04-20 14:54:42 +02002110 /* prepare to detect if the mux needs more room */
2111 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002112
Christopher Faulet61cc8522020-04-20 14:54:42 +02002113 while ((cs->flags & CS_FL_RCV_MORE) ||
2114 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2115 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2116 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2117 cur_read += read;
2118 if (!read ||
2119 (cs->flags & CS_FL_WANT_ROOM) ||
2120 (--read_poll <= 0) ||
2121 (read < max && read >= global.tune.recv_enough))
2122 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002123 }
2124
Christopher Faulet61cc8522020-04-20 14:54:42 +02002125 end_recv:
2126 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2127 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2128 /* Report network errors only if we got no other data. Otherwise
2129 * we'll let the upper layers decide whether the response is OK
2130 * or not. It is very common that an RST sent by the server is
2131 * reported as an error just after the last data chunk.
2132 */
2133 goto stop;
2134 }
2135 if (!cur_read) {
2136 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2137 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2138 goto wait_more_data;
2139 }
2140 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002141 int status;
2142
Christopher Faulet61cc8522020-04-20 14:54:42 +02002143 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2144 tcpcheck_get_step_id(check, rule));
2145 if (rule->comment)
2146 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002147
2148 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2149 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002150 goto stop;
2151 }
2152 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002153
2154 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002155 return ret;
2156
Christopher Faulet61cc8522020-04-20 14:54:42 +02002157 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002158 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002159 goto out;
2160
2161 wait_more_data:
2162 ret = TCPCHK_EVAL_WAIT;
2163 goto out;
2164}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002165
Christopher Faulet61cc8522020-04-20 14:54:42 +02002166/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2167 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2168 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2169 * error occurred.
2170 */
2171static 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 +02002172{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002173 struct htx *htx = htxbuf(&check->bi);
2174 struct htx_sl *sl;
2175 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002176 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002177 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002178 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002179 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002180 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002181 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002182
Christopher Faulet61cc8522020-04-20 14:54:42 +02002183 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002184
Christopher Faulet61cc8522020-04-20 14:54:42 +02002185 if (htx->flags & HTX_FL_PARSING_ERROR) {
2186 status = HCHK_STATUS_L7RSP;
2187 goto error;
2188 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002189
Christopher Faulet61cc8522020-04-20 14:54:42 +02002190 if (htx_is_empty(htx)) {
2191 if (last_read) {
2192 status = HCHK_STATUS_L7RSP;
2193 goto error;
2194 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002195 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002196 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002197
Christopher Faulet61cc8522020-04-20 14:54:42 +02002198 sl = http_get_stline(htx);
2199 check->code = sl->info.res.status;
2200
2201 if (check->server &&
2202 (check->server->proxy->options & PR_O_DISABLE404) &&
2203 (check->server->next_state != SRV_ST_STOPPED) &&
2204 (check->code == 404)) {
2205 /* 404 may be accepted as "stopping" only if the server was up */
2206 goto out;
2207 }
2208
2209 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2210 /* Make GCC happy ; initialize match to a failure state. */
2211 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002212 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002213
2214 switch (expect->type) {
2215 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002216 match = 0;
2217 for (i = 0; i < expect->codes.num; i++) {
2218 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2219 sl->info.res.status <= expect->codes.codes[i][1]) {
2220 match = 1;
2221 break;
2222 }
2223 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002224
2225 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002226 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002227 if (LIST_ISEMPTY(&expect->onerror_fmt))
2228 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002229 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002230 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002231 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2232
2233 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002234 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002235 if (LIST_ISEMPTY(&expect->onerror_fmt))
2236 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002237 break;
2238
Christopher Faulet39708192020-05-05 10:47:36 +02002239 case TCPCHK_EXPECT_HTTP_HEADER: {
2240 struct http_hdr_ctx ctx;
2241 struct ist npat, vpat, value;
2242 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2243
2244 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2245 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002246 if (!nbuf) {
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 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 +02002252 if (!b_data(nbuf)) {
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 npat = ist2(b_orig(nbuf), b_data(nbuf));
2258 }
2259 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2260 npat = expect->hdr.name;
2261
2262 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2263 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002264 if (!vbuf) {
2265 status = HCHK_STATUS_L7RSP;
2266 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002267 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002268 }
Christopher Faulet39708192020-05-05 10:47:36 +02002269 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 +02002270 if (!b_data(vbuf)) {
2271 status = HCHK_STATUS_L7RSP;
2272 desc = ist("log-format string evaluated to an empty string");
2273 goto error;
2274 }
Christopher Faulet39708192020-05-05 10:47:36 +02002275 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2276 }
2277 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2278 vpat = expect->hdr.value;
2279
2280 match = 0;
2281 ctx.blk = NULL;
2282 while (1) {
2283 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2284 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2285 if (!http_find_str_header(htx, npat, &ctx, full))
2286 goto end_of_match;
2287 break;
2288 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2289 if (!http_find_pfx_header(htx, npat, &ctx, full))
2290 goto end_of_match;
2291 break;
2292 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2293 if (!http_find_sfx_header(htx, npat, &ctx, full))
2294 goto end_of_match;
2295 break;
2296 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2297 if (!http_find_sub_header(htx, npat, &ctx, full))
2298 goto end_of_match;
2299 break;
2300 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2301 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2302 goto end_of_match;
2303 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002304 default:
2305 /* should never happen */
2306 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002307 }
2308
Christopher Faulet083eff32020-05-07 15:41:39 +02002309 /* A header has matched the name pattern, let's test its
2310 * value now (always defined from there). If there is no
2311 * value pattern, it is a good match.
2312 */
2313
Christopher Faulet39708192020-05-05 10:47:36 +02002314 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2315 match = 1;
2316 goto end_of_match;
2317 }
2318
2319 value = ctx.value;
2320 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2321 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2322 if (isteq(value, vpat)) {
2323 match = 1;
2324 goto end_of_match;
2325 }
2326 break;
2327 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2328 if (istlen(value) < istlen(vpat))
2329 break;
2330 value = ist2(istptr(value), istlen(vpat));
2331 if (isteq(value, vpat)) {
2332 match = 1;
2333 goto end_of_match;
2334 }
2335 break;
2336 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2337 if (istlen(value) < istlen(vpat))
2338 break;
2339 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2340 if (isteq(value, vpat)) {
2341 match = 1;
2342 goto end_of_match;
2343 }
2344 break;
2345 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2346 if (isttest(istist(value, vpat))) {
2347 match = 1;
2348 goto end_of_match;
2349 }
2350 break;
2351 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2352 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2353 match = 1;
2354 goto end_of_match;
2355 }
2356 break;
2357 }
2358 }
2359
2360 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002361 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002362 if (LIST_ISEMPTY(&expect->onerror_fmt))
2363 desc = htx_sl_res_reason(sl);
2364 break;
2365 }
2366
Christopher Faulet61cc8522020-04-20 14:54:42 +02002367 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002368 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002369 case TCPCHK_EXPECT_HTTP_BODY_LF:
2370 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002371 chunk_reset(&trash);
2372 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2373 enum htx_blk_type type = htx_get_blk_type(blk);
2374
2375 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2376 break;
2377 if (type == HTX_BLK_DATA) {
2378 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2379 break;
2380 }
2381 }
2382
2383 if (!b_data(&trash)) {
2384 if (!last_read)
2385 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002386 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002387 if (LIST_ISEMPTY(&expect->onerror_fmt))
2388 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002389 goto error;
2390 }
2391
Christopher Fauletaaab0832020-05-05 15:54:22 +02002392 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2393 tmp = alloc_trash_chunk();
2394 if (!tmp) {
2395 status = HCHK_STATUS_L7RSP;
2396 desc = ist("Failed to allocate buffer to eval log-format string");
2397 goto error;
2398 }
2399 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2400 if (!b_data(tmp)) {
2401 status = HCHK_STATUS_L7RSP;
2402 desc = ist("log-format string evaluated to an empty string");
2403 goto error;
2404 }
2405 }
2406
Christopher Faulet61cc8522020-04-20 14:54:42 +02002407 if (!last_read &&
2408 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002409 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002410 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2411 ret = TCPCHK_EVAL_WAIT;
2412 goto out;
2413 }
2414
2415 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002416 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002417 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2418 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002419 else
2420 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2421
2422 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002423 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002424 if (LIST_ISEMPTY(&expect->onerror_fmt))
2425 desc = (inverse
2426 ? ist("HTTP check matched unwanted content")
2427 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002428 break;
2429
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002430
Christopher Faulet61cc8522020-04-20 14:54:42 +02002431 default:
2432 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002433 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002434 goto error;
2435 }
2436
Christopher Faulet61cc8522020-04-20 14:54:42 +02002437 /* Wait for more data on mismatch only if no minimum is defined (-1),
2438 * otherwise the absence of match is already conclusive.
2439 */
2440 if (!match && !last_read && (expect->min_recv == -1)) {
2441 ret = TCPCHK_EVAL_WAIT;
2442 goto out;
2443 }
2444
2445 if (!(match ^ inverse))
2446 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002447
2448 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002449 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002450 free_trash_chunk(nbuf);
2451 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002452 free_trash_chunk(msg);
2453 return ret;
2454
2455 error:
2456 ret = TCPCHK_EVAL_STOP;
2457 msg = alloc_trash_chunk();
2458 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002459 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002460 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2461 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002462
2463 wait_more_data:
2464 ret = TCPCHK_EVAL_WAIT;
2465 goto out;
2466}
2467
Christopher Faulet61cc8522020-04-20 14:54:42 +02002468/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2469 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2470 * if an error occurred.
2471 */
2472static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2473{
2474 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2475 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002476 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002477 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002478 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002479 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002480
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002482
Christopher Faulet61cc8522020-04-20 14:54:42 +02002483 /* The current expect might need more data than the previous one, check again
2484 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002485 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002486 if (!last_read) {
2487 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2488 (b_data(&check->bi) < istlen(expect->data))) {
2489 ret = TCPCHK_EVAL_WAIT;
2490 goto out;
2491 }
2492 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2493 ret = TCPCHK_EVAL_WAIT;
2494 goto out;
2495 }
2496 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002497
Christopher Faulet61cc8522020-04-20 14:54:42 +02002498 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2499 /* Make GCC happy ; initialize match to a failure state. */
2500 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002501 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002502
Christopher Faulet61cc8522020-04-20 14:54:42 +02002503 switch (expect->type) {
2504 case TCPCHK_EXPECT_STRING:
2505 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002506 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 +02002507 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002508 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002509 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 +02002510 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002511
Christopher Faulet67a23452020-05-05 18:10:01 +02002512 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002513 chunk_reset(&trash);
2514 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002515 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002516 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002517
2518 case TCPCHK_EXPECT_STRING_LF:
2519 case TCPCHK_EXPECT_BINARY_LF:
2520 match = 0;
2521 tmp = alloc_trash_chunk();
2522 if (!tmp) {
2523 status = HCHK_STATUS_L7RSP;
2524 desc = ist("Failed to allocate buffer to eval format string");
2525 goto error;
2526 }
2527 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2528 if (!b_data(tmp)) {
2529 status = HCHK_STATUS_L7RSP;
2530 desc = ist("log-format string evaluated to an empty string");
2531 goto error;
2532 }
2533 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2534 int len = tmp->data;
2535 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2536 status = HCHK_STATUS_L7RSP;
2537 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2538 goto error;
2539 }
2540 tmp->data = len;
2541 }
2542 if (b_data(&check->bi) < tmp->data) {
2543 if (!last_read) {
2544 ret = TCPCHK_EVAL_WAIT;
2545 goto out;
2546 }
2547 break;
2548 }
2549 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2550 break;
2551
Christopher Faulet61cc8522020-04-20 14:54:42 +02002552 case TCPCHK_EXPECT_CUSTOM:
2553 if (expect->custom)
2554 ret = expect->custom(check, rule, last_read);
2555 goto out;
2556 default:
2557 /* Should never happen. */
2558 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002559 goto out;
2560 }
2561
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002562
Christopher Faulet61cc8522020-04-20 14:54:42 +02002563 /* Wait for more data on mismatch only if no minimum is defined (-1),
2564 * otherwise the absence of match is already conclusive.
2565 */
2566 if (!match && !last_read && (expect->min_recv == -1)) {
2567 ret = TCPCHK_EVAL_WAIT;
2568 goto out;
2569 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002570
Christopher Faulet61cc8522020-04-20 14:54:42 +02002571 /* Result as expected, next rule. */
2572 if (match ^ inverse)
2573 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002574
Christopher Fauletaaab0832020-05-05 15:54:22 +02002575 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002576 /* From this point on, we matched something we did not want, this is an error state. */
2577 ret = TCPCHK_EVAL_STOP;
2578 msg = alloc_trash_chunk();
2579 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002580 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002581 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002582 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002583
Christopher Faulet61cc8522020-04-20 14:54:42 +02002584 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002585 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002586 return ret;
2587}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002588
Christopher Faulet61cc8522020-04-20 14:54:42 +02002589/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002590 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002591 * waits.
2592 */
2593static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2594{
2595 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2596 struct act_rule *act_rule;
2597 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002598
Christopher Faulet61cc8522020-04-20 14:54:42 +02002599 act_rule =rule->action_kw.rule;
2600 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2601 if (act_ret != ACT_RET_CONT) {
2602 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2603 tcpcheck_get_step_id(check, rule));
2604 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2605 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002606 }
2607
Christopher Faulet61cc8522020-04-20 14:54:42 +02002608 return ret;
2609}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002610
Christopher Faulet61cc8522020-04-20 14:54:42 +02002611/* Executes a tcp-check ruleset. Note that this is called both from the
2612 * connection's wake() callback and from the check scheduling task. It returns
2613 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2614 * presenting the risk of an fd replacement.
2615 *
2616 * Please do NOT place any return statement in this function and only leave
2617 * via the out_end_tcpcheck label after setting retcode.
2618 */
2619static int tcpcheck_main(struct check *check)
2620{
2621 struct tcpcheck_rule *rule;
2622 struct conn_stream *cs = check->cs;
2623 struct connection *conn = cs_conn(cs);
2624 int must_read = 1, last_read = 0;
2625 int ret, retcode = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002626
Christopher Faulet61cc8522020-04-20 14:54:42 +02002627 /* here, we know that the check is complete or that it failed */
2628 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002629 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002630
Christopher Faulet61cc8522020-04-20 14:54:42 +02002631 /* 1- check for connection error, if any */
2632 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2633 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002634
Christopher Faulet61cc8522020-04-20 14:54:42 +02002635 /* 2- check if we are waiting for the connection establishment. It only
2636 * happens during TCPCHK_ACT_CONNECT. */
2637 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002638 if (conn && (conn->flags & CO_FL_WAIT_XPRT)) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002639 struct tcpcheck_rule *next;
2640
2641 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2642 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002643 if (!(check->wait_list.events & SUB_RETRY_SEND))
2644 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2645 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002646 else {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002647 if (!(check->wait_list.events & SUB_RETRY_RECV))
2648 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2649 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002650 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002651 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002652 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002653 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002654
2655 /* 3- check for pending outgoing data. It only happens during
2656 * TCPCHK_ACT_SEND. */
2657 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
2658 if (conn && b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002659 /* We're already waiting to be able to send, give up */
2660 if (check->wait_list.events & SUB_RETRY_SEND)
2661 goto out;
2662
Christopher Faulet6d471212020-04-22 11:09:25 +02002663 ret = conn->mux->snd_buf(cs, &check->bo,
2664 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002665 if (ret <= 0) {
2666 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2667 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002668 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002669 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002670 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2671 goto out;
2672 }
2673 }
2674 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002675 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002676
Christopher Faulet61cc8522020-04-20 14:54:42 +02002677 /* 4- check if a rule must be resume. It happens if check->current_step
2678 * is defined. */
2679 else if (check->current_step)
2680 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002681
Christopher Faulet61cc8522020-04-20 14:54:42 +02002682 /* 5- It is the first evaluation. We must create a session and preset
2683 * tcp-check variables */
2684 else {
2685 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002686
Christopher Faulet61cc8522020-04-20 14:54:42 +02002687 /* First evaluation, create a session */
2688 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2689 if (!check->sess) {
2690 chunk_printf(&trash, "TCPCHK error allocating check session");
2691 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2692 goto out_end_tcpcheck;
2693 }
2694 vars_init(&check->vars, SCOPE_CHECK);
2695 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002696
Christopher Faulet61cc8522020-04-20 14:54:42 +02002697 /* Preset tcp-check variables */
2698 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2699 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002700
Christopher Faulet61cc8522020-04-20 14:54:42 +02002701 memset(&smp, 0, sizeof(smp));
2702 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2703 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002704 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002705 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002706 }
2707
Christopher Faulet61cc8522020-04-20 14:54:42 +02002708 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002709
Christopher Faulet61cc8522020-04-20 14:54:42 +02002710 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
2711 enum tcpcheck_eval_ret eval_ret;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002712
Christopher Faulet61cc8522020-04-20 14:54:42 +02002713 check->code = 0;
2714 switch (rule->action) {
2715 case TCPCHK_ACT_CONNECT:
2716 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002717
Christopher Faulet61cc8522020-04-20 14:54:42 +02002718 /* close but not release yet previous connection */
2719 if (check->cs) {
2720 cs_close(check->cs);
2721 retcode = -1; /* do not reuse the fd in the caller! */
2722 }
2723 eval_ret = tcpcheck_eval_connect(check, rule);
2724 must_read = 1; last_read = 0;
2725 break;
2726 case TCPCHK_ACT_SEND:
2727 check->current_step = rule;
2728 eval_ret = tcpcheck_eval_send(check, rule);
2729 must_read = 1;
2730 break;
2731 case TCPCHK_ACT_EXPECT:
2732 check->current_step = rule;
2733 if (must_read) {
2734 if (check->proxy->timeout.check)
2735 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002736
Christopher Faulet61cc8522020-04-20 14:54:42 +02002737 eval_ret = tcpcheck_eval_recv(check, rule);
2738 if (eval_ret == TCPCHK_EVAL_STOP)
2739 goto out_end_tcpcheck;
2740 else if (eval_ret == TCPCHK_EVAL_WAIT)
2741 goto out;
2742 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2743 must_read = 0;
2744 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002745
Christopher Faulet61cc8522020-04-20 14:54:42 +02002746 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2747 ? tcpcheck_eval_expect_http(check, rule, last_read)
2748 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002749
Christopher Faulet61cc8522020-04-20 14:54:42 +02002750 if (eval_ret == TCPCHK_EVAL_WAIT) {
2751 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002752 if (!(check->wait_list.events & SUB_RETRY_RECV))
2753 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002754 }
2755 break;
2756 case TCPCHK_ACT_ACTION_KW:
2757 /* Don't update the current step */
2758 eval_ret = tcpcheck_eval_action_kw(check, rule);
2759 break;
2760 default:
2761 /* Otherwise, just go to the next one and don't update
2762 * the current step
2763 */
2764 eval_ret = TCPCHK_EVAL_CONTINUE;
2765 break;
2766 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002767
Christopher Faulet61cc8522020-04-20 14:54:42 +02002768 switch (eval_ret) {
2769 case TCPCHK_EVAL_CONTINUE:
2770 break;
2771 case TCPCHK_EVAL_WAIT:
2772 goto out;
2773 case TCPCHK_EVAL_STOP:
2774 goto out_end_tcpcheck;
2775 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002776 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002777
Christopher Faulet61cc8522020-04-20 14:54:42 +02002778 /* All rules was evaluated */
2779 if (check->current_step) {
2780 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002781
Christopher Faulet61cc8522020-04-20 14:54:42 +02002782 if (rule->action == TCPCHK_ACT_EXPECT) {
2783 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002784 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002785
Christopher Faulet61cc8522020-04-20 14:54:42 +02002786 if (check->server &&
2787 (check->server->proxy->options & PR_O_DISABLE404) &&
2788 (check->server->next_state != SRV_ST_STOPPED) &&
2789 (check->code == 404)) {
2790 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2791 goto out_end_tcpcheck;
2792 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002793
Christopher Faulet61cc8522020-04-20 14:54:42 +02002794 msg = alloc_trash_chunk();
2795 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002796 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002797 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2798 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002799 free_trash_chunk(msg);
2800 }
2801 else if (rule->action == TCPCHK_ACT_CONNECT) {
2802 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002803 enum healthcheck_status status = HCHK_STATUS_L4OK;
2804#ifdef USE_OPENSSL
2805 if (conn && ssl_sock_is_ssl(conn))
2806 status = HCHK_STATUS_L6OK;
2807#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002808 set_server_check_status(check, status, msg);
2809 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002810 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002811 else
2812 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002813
Christopher Faulet61cc8522020-04-20 14:54:42 +02002814 out_end_tcpcheck:
2815 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2816 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002817
Christopher Faulet61cc8522020-04-20 14:54:42 +02002818 out:
2819 return retcode;
2820}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002821
Christopher Faulet14cd3162020-04-16 14:50:06 +02002822
Christopher Faulet61cc8522020-04-20 14:54:42 +02002823/**************************************************************************/
2824/************** Health-checks based on an external process ****************/
2825/**************************************************************************/
2826static struct list pid_list = LIST_HEAD_INIT(pid_list);
2827static struct pool_head *pool_head_pid_list;
2828__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002829
Christopher Faulet61cc8522020-04-20 14:54:42 +02002830struct extcheck_env {
2831 char *name; /* environment variable name */
2832 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2833};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002834
Christopher Faulet61cc8522020-04-20 14:54:42 +02002835/* environment variables memory requirement for different types of data */
2836#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2837 * such environment variables are not updatable. */
2838#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2839#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2840#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002841
Christopher Faulet61cc8522020-04-20 14:54:42 +02002842/* external checks environment variables */
2843enum {
2844 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002845
Christopher Faulet61cc8522020-04-20 14:54:42 +02002846 /* Proxy specific environment variables */
2847 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2848 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2849 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2850 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002851
Christopher Faulet61cc8522020-04-20 14:54:42 +02002852 /* Server specific environment variables */
2853 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2854 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2855 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2856 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2857 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2858 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002859
Christopher Faulet61cc8522020-04-20 14:54:42 +02002860 EXTCHK_SIZE
2861};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002862
Christopher Faulet61cc8522020-04-20 14:54:42 +02002863const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2864 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2865 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2866 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2867 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2868 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2869 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2870 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2871 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2872 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2873 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2874 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2875};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002876
Christopher Faulet61cc8522020-04-20 14:54:42 +02002877void block_sigchld(void)
2878{
2879 sigset_t set;
2880 sigemptyset(&set);
2881 sigaddset(&set, SIGCHLD);
2882 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2883}
Willy Tarreaube373152018-09-06 11:45:30 +02002884
Christopher Faulet61cc8522020-04-20 14:54:42 +02002885void unblock_sigchld(void)
2886{
2887 sigset_t set;
2888 sigemptyset(&set);
2889 sigaddset(&set, SIGCHLD);
2890 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002891}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002892
Christopher Faulet61cc8522020-04-20 14:54:42 +02002893static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002894{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002895 struct pid_list *elem;
2896 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002897
Christopher Faulet61cc8522020-04-20 14:54:42 +02002898 elem = pool_alloc(pool_head_pid_list);
2899 if (!elem)
2900 return NULL;
2901 elem->pid = pid;
2902 elem->t = t;
2903 elem->exited = 0;
2904 check->curpid = elem;
2905 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002906
Christopher Faulet61cc8522020-04-20 14:54:42 +02002907 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2908 LIST_ADD(&pid_list, &elem->list);
2909 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002910
Christopher Faulet61cc8522020-04-20 14:54:42 +02002911 return elem;
2912}
Christopher Faulete5870d82020-04-15 11:32:03 +02002913
Christopher Faulet61cc8522020-04-20 14:54:42 +02002914static void pid_list_del(struct pid_list *elem)
2915{
2916 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002917
Christopher Faulet61cc8522020-04-20 14:54:42 +02002918 if (!elem)
2919 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002920
Christopher Faulet61cc8522020-04-20 14:54:42 +02002921 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2922 LIST_DEL(&elem->list);
2923 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002924
Christopher Faulet61cc8522020-04-20 14:54:42 +02002925 if (!elem->exited)
2926 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002927
Christopher Faulet61cc8522020-04-20 14:54:42 +02002928 check = elem->t->context;
2929 check->curpid = NULL;
2930 pool_free(pool_head_pid_list, elem);
2931}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002932
Christopher Faulet61cc8522020-04-20 14:54:42 +02002933/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2934static void pid_list_expire(pid_t pid, int status)
2935{
2936 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002937
Christopher Faulet61cc8522020-04-20 14:54:42 +02002938 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2939 list_for_each_entry(elem, &pid_list, list) {
2940 if (elem->pid == pid) {
2941 elem->t->expire = now_ms;
2942 elem->status = status;
2943 elem->exited = 1;
2944 task_wakeup(elem->t, TASK_WOKEN_IO);
2945 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002946 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002947 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002948 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2949}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002950
Christopher Faulet61cc8522020-04-20 14:54:42 +02002951static void sigchld_handler(struct sig_handler *sh)
2952{
2953 pid_t pid;
2954 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002955
Christopher Faulet61cc8522020-04-20 14:54:42 +02002956 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2957 pid_list_expire(pid, status);
2958}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002959
Christopher Faulet61cc8522020-04-20 14:54:42 +02002960static int init_pid_list(void)
2961{
2962 if (pool_head_pid_list != NULL)
2963 /* Nothing to do */
2964 return 0;
2965
2966 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2967 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2968 strerror(errno));
2969 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002970 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002971
Christopher Faulet61cc8522020-04-20 14:54:42 +02002972 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2973 if (pool_head_pid_list == NULL) {
2974 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2975 strerror(errno));
2976 return 1;
2977 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002978
Christopher Faulet61cc8522020-04-20 14:54:42 +02002979 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002980}
2981
Christopher Faulet61cc8522020-04-20 14:54:42 +02002982/* helper macro to set an environment variable and jump to a specific label on failure. */
2983#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002984
Christopher Faulet61cc8522020-04-20 14:54:42 +02002985/*
2986 * helper function to allocate enough memory to store an environment variable.
2987 * It will also check that the environment variable is updatable, and silently
2988 * fail if not.
2989 */
2990static int extchk_setenv(struct check *check, int idx, const char *value)
2991{
2992 int len, ret;
2993 char *envname;
2994 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002995
Christopher Faulet61cc8522020-04-20 14:54:42 +02002996 if (idx < 0 || idx >= EXTCHK_SIZE) {
2997 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
2998 return 1;
2999 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003000
Christopher Faulet61cc8522020-04-20 14:54:42 +02003001 envname = extcheck_envs[idx].name;
3002 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003003
Christopher Faulet61cc8522020-04-20 14:54:42 +02003004 /* Check if the environment variable is already set, and silently reject
3005 * the update if this one is not updatable. */
3006 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3007 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003008
Christopher Faulet61cc8522020-04-20 14:54:42 +02003009 /* Instead of sending NOT_USED, sending an empty value is preferable */
3010 if (strcmp(value, "NOT_USED") == 0) {
3011 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003012 }
3013
Christopher Faulet61cc8522020-04-20 14:54:42 +02003014 len = strlen(envname) + 1;
3015 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3016 len += strlen(value);
3017 else
3018 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003019
Christopher Faulet61cc8522020-04-20 14:54:42 +02003020 if (!check->envp[idx])
3021 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003022
Christopher Faulet61cc8522020-04-20 14:54:42 +02003023 if (!check->envp[idx]) {
3024 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3025 return 1;
3026 }
3027 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3028 if (ret < 0) {
3029 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3030 return 1;
3031 }
3032 else if (ret > len) {
3033 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3034 return 1;
3035 }
3036 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003037}
3038
Christopher Faulet61cc8522020-04-20 14:54:42 +02003039static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003040{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003041 struct server *s = check->server;
3042 struct proxy *px = s->proxy;
3043 struct listener *listener = NULL, *l;
3044 int i;
3045 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3046 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003047
Christopher Faulet61cc8522020-04-20 14:54:42 +02003048 list_for_each_entry(l, &px->conf.listeners, by_fe)
3049 /* Use the first INET, INET6 or UNIX listener */
3050 if (l->addr.ss_family == AF_INET ||
3051 l->addr.ss_family == AF_INET6 ||
3052 l->addr.ss_family == AF_UNIX) {
3053 listener = l;
3054 break;
3055 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003056
Christopher Faulet61cc8522020-04-20 14:54:42 +02003057 check->curpid = NULL;
3058 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3059 if (!check->envp) {
3060 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3061 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003062 }
3063
Christopher Faulet61cc8522020-04-20 14:54:42 +02003064 check->argv = calloc(6, sizeof(char *));
3065 if (!check->argv) {
3066 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3067 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003068 }
3069
Christopher Faulet61cc8522020-04-20 14:54:42 +02003070 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003071
Christopher Faulet61cc8522020-04-20 14:54:42 +02003072 if (!listener) {
3073 check->argv[1] = strdup("NOT_USED");
3074 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003075 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003076 else if (listener->addr.ss_family == AF_INET ||
3077 listener->addr.ss_family == AF_INET6) {
3078 addr_to_str(&listener->addr, buf, sizeof(buf));
3079 check->argv[1] = strdup(buf);
3080 port_to_str(&listener->addr, buf, sizeof(buf));
3081 check->argv[2] = strdup(buf);
3082 }
3083 else if (listener->addr.ss_family == AF_UNIX) {
3084 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003085
Christopher Faulet61cc8522020-04-20 14:54:42 +02003086 un = (struct sockaddr_un *)&listener->addr;
3087 check->argv[1] = strdup(un->sun_path);
3088 check->argv[2] = strdup("NOT_USED");
3089 }
3090 else {
3091 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3092 goto err;
3093 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003094
Christopher Faulet61cc8522020-04-20 14:54:42 +02003095 if (!check->argv[1] || !check->argv[2]) {
3096 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3097 goto err;
3098 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003099
Christopher Faulet61cc8522020-04-20 14:54:42 +02003100 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3101 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3102 if (!check->argv[3] || !check->argv[4]) {
3103 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3104 goto err;
3105 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003106
Christopher Faulet61cc8522020-04-20 14:54:42 +02003107 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3108 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3109 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003110
Christopher Faulet61cc8522020-04-20 14:54:42 +02003111 for (i = 0; i < 5; i++) {
3112 if (!check->argv[i]) {
3113 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3114 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003115 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003116 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003117
Christopher Faulet61cc8522020-04-20 14:54:42 +02003118 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3119 /* Add proxy environment variables */
3120 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3121 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3122 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3123 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3124 /* Add server environment variables */
3125 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3126 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3127 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3128 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3129 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3130 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003131
Christopher Faulet61cc8522020-04-20 14:54:42 +02003132 /* Ensure that we don't leave any hole in check->envp */
3133 for (i = 0; i < EXTCHK_SIZE; i++)
3134 if (!check->envp[i])
3135 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003136
Christopher Faulet61cc8522020-04-20 14:54:42 +02003137 return 1;
3138err:
3139 if (check->envp) {
3140 for (i = 0; i < EXTCHK_SIZE; i++)
3141 free(check->envp[i]);
3142 free(check->envp);
3143 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003144 }
3145
Christopher Faulet61cc8522020-04-20 14:54:42 +02003146 if (check->argv) {
3147 for (i = 1; i < 5; i++)
3148 free(check->argv[i]);
3149 free(check->argv);
3150 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003151 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003152 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003153}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003154
Christopher Faulet61cc8522020-04-20 14:54:42 +02003155/*
3156 * establish a server health-check that makes use of a process.
3157 *
3158 * It can return one of :
3159 * - SF_ERR_NONE if everything's OK
3160 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3161 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3162 *
3163 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003164 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003165static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003166{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003167 char buf[256];
3168 struct check *check = t->context;
3169 struct server *s = check->server;
3170 struct proxy *px = s->proxy;
3171 int status;
3172 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003173
Christopher Faulet61cc8522020-04-20 14:54:42 +02003174 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003175
Christopher Faulet61cc8522020-04-20 14:54:42 +02003176 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003177
Christopher Faulet61cc8522020-04-20 14:54:42 +02003178 pid = fork();
3179 if (pid < 0) {
3180 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3181 (global.tune.options & GTUNE_INSECURE_FORK) ?
3182 "" : " (likely caused by missing 'insecure-fork-wanted')",
3183 strerror(errno));
3184 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003185 goto out;
3186 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003187 if (pid == 0) {
3188 /* Child */
3189 extern char **environ;
3190 struct rlimit limit;
3191 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003192
Christopher Faulet61cc8522020-04-20 14:54:42 +02003193 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3194 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003195
Christopher Faulet61cc8522020-04-20 14:54:42 +02003196 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003197
Christopher Faulet61cc8522020-04-20 14:54:42 +02003198 /* restore the initial FD limits */
3199 limit.rlim_cur = rlim_fd_cur_at_boot;
3200 limit.rlim_max = rlim_fd_max_at_boot;
3201 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3202 getrlimit(RLIMIT_NOFILE, &limit);
3203 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3204 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3205 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3206 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003207
Christopher Faulet61cc8522020-04-20 14:54:42 +02003208 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003209
Christopher Faulet61cc8522020-04-20 14:54:42 +02003210 /* Update some environment variables and command args: curconn, server addr and server port */
3211 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003212
Christopher Faulet61cc8522020-04-20 14:54:42 +02003213 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3214 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003215
Christopher Faulet61cc8522020-04-20 14:54:42 +02003216 *check->argv[4] = 0;
3217 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3218 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3219 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003220
Christopher Faulet61cc8522020-04-20 14:54:42 +02003221 haproxy_unblock_signals();
3222 execvp(px->check_command, check->argv);
3223 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3224 strerror(errno));
3225 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003226 }
3227
Christopher Faulet61cc8522020-04-20 14:54:42 +02003228 /* Parent */
3229 if (check->result == CHK_RES_UNKNOWN) {
3230 if (pid_list_add(pid, t) != NULL) {
3231 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3232
3233 if (px->timeout.check && px->timeout.connect) {
3234 int t_con = tick_add(now_ms, px->timeout.connect);
3235 t->expire = tick_first(t->expire, t_con);
3236 }
3237 status = SF_ERR_NONE;
3238 goto out;
3239 }
3240 else {
3241 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3242 }
3243 kill(pid, SIGTERM); /* process creation error */
3244 }
3245 else
3246 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3247
3248out:
3249 unblock_sigchld();
3250 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003251}
3252
Christopher Faulet61cc8522020-04-20 14:54:42 +02003253/*
3254 * manages a server health-check that uses an external process. Returns
3255 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003256 *
3257 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003258 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003259 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003260static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003261{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003262 struct check *check = context;
3263 struct server *s = check->server;
3264 int rv;
3265 int ret;
3266 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003267
Christopher Faulet61cc8522020-04-20 14:54:42 +02003268 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3269 if (!(check->state & CHK_ST_INPROGRESS)) {
3270 /* no check currently running */
3271 if (!expired) /* woke up too early */
3272 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003273
Christopher Faulet61cc8522020-04-20 14:54:42 +02003274 /* we don't send any health-checks when the proxy is
3275 * stopped, the server should not be checked or the check
3276 * is disabled.
3277 */
3278 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3279 s->proxy->state == PR_STSTOPPED)
3280 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003281
Christopher Faulet61cc8522020-04-20 14:54:42 +02003282 /* we'll initiate a new check */
3283 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003284
Christopher Faulet61cc8522020-04-20 14:54:42 +02003285 check->state |= CHK_ST_INPROGRESS;
3286
3287 ret = connect_proc_chk(t);
3288 if (ret == SF_ERR_NONE) {
3289 /* the process was forked, we allow up to min(inter,
3290 * timeout.connect) for it to report its status, but
3291 * only when timeout.check is set as it may be to short
3292 * for a full check otherwise.
3293 */
3294 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3295
3296 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3297 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3298 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003299 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003300 task_set_affinity(t, tid_bit);
3301 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003302 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003303
Christopher Faulet61cc8522020-04-20 14:54:42 +02003304 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003305
Christopher Faulet61cc8522020-04-20 14:54:42 +02003306 check->state &= ~CHK_ST_INPROGRESS;
3307 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003308
Christopher Faulet61cc8522020-04-20 14:54:42 +02003309 /* we allow up to min(inter, timeout.connect) for a connection
3310 * to establish but only when timeout.check is set
3311 * as it may be to short for a full check otherwise
3312 */
3313 while (tick_is_expired(t->expire, now_ms)) {
3314 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003315
Christopher Faulet61cc8522020-04-20 14:54:42 +02003316 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3317 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003318
Christopher Faulet61cc8522020-04-20 14:54:42 +02003319 if (s->proxy->timeout.check)
3320 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003321 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003322 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003323 else {
3324 /* there was a test running.
3325 * First, let's check whether there was an uncaught error,
3326 * which can happen on connect timeout or error.
3327 */
3328 if (check->result == CHK_RES_UNKNOWN) {
3329 /* good connection is enough for pure TCP check */
3330 struct pid_list *elem = check->curpid;
3331 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003332
Christopher Faulet61cc8522020-04-20 14:54:42 +02003333 if (elem->exited) {
3334 status = elem->status; /* Save in case the process exits between use below */
3335 if (!WIFEXITED(status))
3336 check->code = -1;
3337 else
3338 check->code = WEXITSTATUS(status);
3339 if (!WIFEXITED(status) || WEXITSTATUS(status))
3340 status = HCHK_STATUS_PROCERR;
3341 else
3342 status = HCHK_STATUS_PROCOK;
3343 } else if (expired) {
3344 status = HCHK_STATUS_PROCTOUT;
3345 ha_warning("kill %d\n", (int)elem->pid);
3346 kill(elem->pid, SIGTERM);
3347 }
3348 set_server_check_status(check, status, NULL);
3349 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003350
Christopher Faulet61cc8522020-04-20 14:54:42 +02003351 if (check->result == CHK_RES_FAILED) {
3352 /* a failure or timeout detected */
3353 check_notify_failure(check);
3354 }
3355 else if (check->result == CHK_RES_CONDPASS) {
3356 /* check is OK but asks for stopping mode */
3357 check_notify_stopping(check);
3358 }
3359 else if (check->result == CHK_RES_PASSED) {
3360 /* a success was detected */
3361 check_notify_success(check);
3362 }
3363 task_set_affinity(t, 1);
3364 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003365
Christopher Faulet61cc8522020-04-20 14:54:42 +02003366 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003367
Christopher Faulet61cc8522020-04-20 14:54:42 +02003368 rv = 0;
3369 if (global.spread_checks > 0) {
3370 rv = srv_getinter(check) * global.spread_checks / 100;
3371 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3372 }
3373 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3374 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003375
Christopher Faulet61cc8522020-04-20 14:54:42 +02003376 reschedule:
3377 while (tick_is_expired(t->expire, now_ms))
3378 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003379
Christopher Faulet61cc8522020-04-20 14:54:42 +02003380 out_unlock:
3381 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3382 return t;
3383}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003384
Baptiste Assmann248f1172018-03-01 21:49:01 +01003385
Christopher Faulet61cc8522020-04-20 14:54:42 +02003386/**************************************************************************/
3387/***************** Health-checks based on connections *********************/
3388/**************************************************************************/
3389/* This function is used only for server health-checks. It handles connection
3390 * status updates including errors. If necessary, it wakes the check task up.
3391 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3392 * connection (eg: reconnect). It relies on tcpcheck_main().
3393 */
3394static int wake_srv_chk(struct conn_stream *cs)
3395{
3396 struct connection *conn = cs->conn;
3397 struct check *check = cs->data;
3398 struct email_alertq *q = container_of(check, typeof(*q), check);
3399 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003400
Christopher Faulet61cc8522020-04-20 14:54:42 +02003401 if (check->server)
3402 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3403 else
3404 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003405
Christopher Faulet61cc8522020-04-20 14:54:42 +02003406 /* we may have to make progress on the TCP checks */
3407 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003408
Christopher Faulet61cc8522020-04-20 14:54:42 +02003409 cs = check->cs;
3410 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003411
Christopher Faulet61cc8522020-04-20 14:54:42 +02003412 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3413 /* We may get error reports bypassing the I/O handlers, typically
3414 * the case when sending a pure TCP check which fails, then the I/O
3415 * handlers above are not called. This is completely handled by the
3416 * main processing task so let's simply wake it up. If we get here,
3417 * we expect errno to still be valid.
3418 */
3419 chk_report_conn_err(check, errno, 0);
3420 task_wakeup(check->task, TASK_WOKEN_IO);
3421 }
3422
3423 if (check->result != CHK_RES_UNKNOWN) {
3424 /* Check complete or aborted. If connection not yet closed do it
3425 * now and wake the check task up to be sure the result is
3426 * handled ASAP. */
3427 conn_sock_drain(conn);
3428 cs_close(cs);
3429 ret = -1;
3430 /* We may have been scheduled to run, and the
3431 * I/O handler expects to have a cs, so remove
3432 * the tasklet
3433 */
3434 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3435 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003436 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003437
3438 if (check->server)
3439 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003440 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003441 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003442
Christopher Faulet61cc8522020-04-20 14:54:42 +02003443 /* if a connection got replaced, we must absolutely prevent the connection
3444 * handler from touching its fd, and perform the FD polling updates ourselves
3445 */
3446 if (ret < 0)
3447 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003448
Christopher Faulet61cc8522020-04-20 14:54:42 +02003449 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003450}
3451
Christopher Faulet61cc8522020-04-20 14:54:42 +02003452/* This function checks if any I/O is wanted, and if so, attempts to do so */
3453static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003454{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003455 struct check *check = ctx;
3456 struct conn_stream *cs = check->cs;
3457 struct email_alertq *q = container_of(check, typeof(*q), check);
3458 int ret = 0;
Simon Hormanb1900d52015-01-30 11:22:54 +09003459
Christopher Faulet61cc8522020-04-20 14:54:42 +02003460 if (!(check->wait_list.events & SUB_RETRY_SEND))
3461 ret = wake_srv_chk(cs);
3462 if (ret == 0 && !(check->wait_list.events & SUB_RETRY_RECV)) {
3463 if (check->server)
3464 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3465 else
3466 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Hormanb1900d52015-01-30 11:22:54 +09003467
Christopher Faulet61cc8522020-04-20 14:54:42 +02003468 if (unlikely(check->result == CHK_RES_FAILED)) {
3469 /* collect possible new errors */
3470 if (cs->conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
3471 chk_report_conn_err(check, 0, 0);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003472
Christopher Faulet61cc8522020-04-20 14:54:42 +02003473 /* Reset the check buffer... */
3474 b_reset(&check->bi);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003475
Christopher Faulet61cc8522020-04-20 14:54:42 +02003476 /* Close the connection... We still attempt to nicely close if,
3477 * for instance, SSL needs to send a "close notify." Later, we perform
3478 * a hard close and reset the connection if some data are pending,
3479 * otherwise we end up with many TIME_WAITs and eat all the source port
3480 * range quickly. To avoid sending RSTs all the time, we first try to
3481 * drain pending data.
3482 */
3483 /* Call cs_shutr() first, to add the CO_FL_SOCK_RD_SH flag on the
3484 * connection, to make sure cs_shutw() will not lead to a shutdown()
3485 * that would provoke TIME_WAITs.
3486 */
3487 cs_shutr(cs, CS_SHR_DRAIN);
3488 cs_shutw(cs, CS_SHW_NORMAL);
Simon Hormanb1900d52015-01-30 11:22:54 +09003489
Christopher Faulet61cc8522020-04-20 14:54:42 +02003490 /* OK, let's not stay here forever */
3491 if (check->result == CHK_RES_FAILED)
3492 cs->conn->flags |= CO_FL_ERROR;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01003493
Christopher Faulet61cc8522020-04-20 14:54:42 +02003494 task_wakeup(t, TASK_WOKEN_IO);
3495 }
3496
3497 if (check->server)
3498 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3499 else
3500 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet23d86d12018-01-25 11:36:35 +01003501 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003502 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003503}
3504
Christopher Faulet61cc8522020-04-20 14:54:42 +02003505/* manages a server health-check that uses a connection. Returns
3506 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3507 *
3508 * Please do NOT place any return statement in this function and only leave
3509 * via the out_unlock label.
3510 */
3511static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003512{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003513 struct check *check = context;
3514 struct proxy *proxy = check->proxy;
3515 struct conn_stream *cs = check->cs;
3516 struct connection *conn = cs_conn(cs);
3517 int rv;
3518 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003519
Christopher Faulet61cc8522020-04-20 14:54:42 +02003520 if (check->server)
3521 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3522 if (!(check->state & CHK_ST_INPROGRESS)) {
3523 /* no check currently running */
3524 if (!expired) /* woke up too early */
3525 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003526
Christopher Faulet61cc8522020-04-20 14:54:42 +02003527 /* we don't send any health-checks when the proxy is
3528 * stopped, the server should not be checked or the check
3529 * is disabled.
3530 */
3531 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3532 proxy->state == PR_STSTOPPED)
3533 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003534
Christopher Faulet61cc8522020-04-20 14:54:42 +02003535 /* we'll initiate a new check */
3536 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003537
Christopher Faulet61cc8522020-04-20 14:54:42 +02003538 check->state |= CHK_ST_INPROGRESS;
3539 b_reset(&check->bi);
3540 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003541
Christopher Faulet61cc8522020-04-20 14:54:42 +02003542 task_set_affinity(t, tid_bit);
3543 cs = check->cs;
3544 conn = cs_conn(cs);
3545 if (!conn) {
3546 check->current_step = NULL;
3547 tcpcheck_main(check);
3548 goto out_unlock;
3549 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003550
Christopher Faulet61cc8522020-04-20 14:54:42 +02003551 conn->flags |= CO_FL_ERROR;
3552 chk_report_conn_err(check, 0, 0);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003553
Christopher Faulet61cc8522020-04-20 14:54:42 +02003554 /* here, we have seen a synchronous error, no fd was allocated */
3555 task_set_affinity(t, MAX_THREADS_MASK);
3556 if (cs) {
3557 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003558 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003559 /* We may have been scheduled to run, and the
3560 * I/O handler expects to have a cs, so remove
3561 * the tasklet
3562 */
3563 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3564 cs_destroy(cs);
3565 cs = check->cs = NULL;
3566 conn = NULL;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003567 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003568
Christopher Faulet61cc8522020-04-20 14:54:42 +02003569 check->state &= ~CHK_ST_INPROGRESS;
3570 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003571
Christopher Faulet61cc8522020-04-20 14:54:42 +02003572 /* we allow up to min(inter, timeout.connect) for a connection
3573 * to establish but only when timeout.check is set
3574 * as it may be to short for a full check otherwise
3575 */
3576 while (tick_is_expired(t->expire, now_ms)) {
3577 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003578
Christopher Faulet61cc8522020-04-20 14:54:42 +02003579 t_con = tick_add(t->expire, proxy->timeout.connect);
3580 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3581 if (proxy->timeout.check)
3582 t->expire = tick_first(t->expire, t_con);
3583 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003584 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003585 else {
3586 /* there was a test running.
3587 * First, let's check whether there was an uncaught error,
3588 * which can happen on connect timeout or error.
3589 */
3590 if (check->result == CHK_RES_UNKNOWN) {
3591 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3592 chk_report_conn_err(check, 0, expired);
3593 }
3594 else
3595 goto out_unlock; /* timeout not reached, wait again */
3596 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003597
Christopher Faulet61cc8522020-04-20 14:54:42 +02003598 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003599
Christopher Faulet61cc8522020-04-20 14:54:42 +02003600 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003601
Christopher Faulet61cc8522020-04-20 14:54:42 +02003602 if (conn && conn->xprt) {
3603 /* The check was aborted and the connection was not yet closed.
3604 * This can happen upon timeout, or when an external event such
3605 * as a failed response coupled with "observe layer7" caused the
3606 * server state to be suddenly changed.
3607 */
3608 conn_sock_drain(conn);
3609 cs_close(cs);
3610 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003611
Christopher Faulet61cc8522020-04-20 14:54:42 +02003612 if (cs) {
3613 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003614 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003615 /* We may have been scheduled to run, and the
3616 * I/O handler expects to have a cs, so remove
3617 * the tasklet
3618 */
3619 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3620 cs_destroy(cs);
3621 cs = check->cs = NULL;
3622 conn = NULL;
3623 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003624
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003625 if (check->sess != NULL) {
3626 vars_prune(&check->vars, check->sess, NULL);
3627 session_free(check->sess);
3628 check->sess = NULL;
3629 }
3630
Christopher Faulet61cc8522020-04-20 14:54:42 +02003631 if (check->server) {
3632 if (check->result == CHK_RES_FAILED) {
3633 /* a failure or timeout detected */
3634 check_notify_failure(check);
3635 }
3636 else if (check->result == CHK_RES_CONDPASS) {
3637 /* check is OK but asks for stopping mode */
3638 check_notify_stopping(check);
3639 }
3640 else if (check->result == CHK_RES_PASSED) {
3641 /* a success was detected */
3642 check_notify_success(check);
3643 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003644 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003645 task_set_affinity(t, MAX_THREADS_MASK);
3646 check->state &= ~CHK_ST_INPROGRESS;
3647
3648 if (check->server) {
3649 rv = 0;
3650 if (global.spread_checks > 0) {
3651 rv = srv_getinter(check) * global.spread_checks / 100;
3652 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3653 }
3654 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003655 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003656 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003657
Christopher Faulet61cc8522020-04-20 14:54:42 +02003658 reschedule:
3659 while (tick_is_expired(t->expire, now_ms))
3660 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3661 out_unlock:
3662 if (check->server)
3663 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3664 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003665}
3666
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003667
Christopher Faulet61cc8522020-04-20 14:54:42 +02003668/**************************************************************************/
3669/******************* Internals to parse tcp-check rules *******************/
3670/**************************************************************************/
3671struct action_kw_list tcp_check_keywords = {
3672 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3673};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003674
Christopher Faulet61cc8522020-04-20 14:54:42 +02003675/* Return the struct action_kw associated to a keyword */
3676static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003677{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003678 return action_lookup(&tcp_check_keywords.list, kw);
3679}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003680
Christopher Faulet61cc8522020-04-20 14:54:42 +02003681static void action_kw_tcp_check_build_list(struct buffer *chk)
3682{
3683 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003684}
3685
Christopher Faulet61cc8522020-04-20 14:54:42 +02003686/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3687 * returned on error.
3688 */
3689static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3690 struct list *rules, struct action_kw *kw,
3691 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003692{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003693 struct tcpcheck_rule *chk = NULL;
3694 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003695
Christopher Faulet61cc8522020-04-20 14:54:42 +02003696 actrule = calloc(1, sizeof(*actrule));
3697 if (!actrule) {
3698 memprintf(errmsg, "out of memory");
3699 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003700 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003701 actrule->kw = kw;
3702 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003703
Christopher Faulet61cc8522020-04-20 14:54:42 +02003704 cur_arg++;
3705 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3706 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3707 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003708 }
3709
Christopher Faulet61cc8522020-04-20 14:54:42 +02003710 chk = calloc(1, sizeof(*chk));
3711 if (!chk) {
3712 memprintf(errmsg, "out of memory");
3713 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003714 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003715 chk->action = TCPCHK_ACT_ACTION_KW;
3716 chk->action_kw.rule = actrule;
3717 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003718
3719 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003720 free(actrule);
3721 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003722}
3723
Christopher Faulet61cc8522020-04-20 14:54:42 +02003724/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3725 * returned on error.
3726 */
3727static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3728 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003729{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003730 struct tcpcheck_rule *chk = NULL;
3731 struct sockaddr_storage *sk = NULL;
3732 char *comment = NULL, *sni = NULL, *alpn = NULL;
3733 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003734 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003735 unsigned short conn_opts = 0;
3736 long port = 0;
3737 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003738
Christopher Faulet61cc8522020-04-20 14:54:42 +02003739 list_for_each_entry(chk, rules, list) {
3740 if (chk->action == TCPCHK_ACT_CONNECT)
3741 break;
3742 if (chk->action == TCPCHK_ACT_COMMENT ||
3743 chk->action == TCPCHK_ACT_ACTION_KW ||
3744 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3745 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003746
Christopher Faulet61cc8522020-04-20 14:54:42 +02003747 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003748 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003749 "when there is a 'connect' step in the tcp-check ruleset");
3750 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003751 }
3752
Christopher Faulet61cc8522020-04-20 14:54:42 +02003753 cur_arg++;
3754 while (*(args[cur_arg])) {
3755 if (strcmp(args[cur_arg], "default") == 0)
3756 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3757 else if (strcmp(args[cur_arg], "addr") == 0) {
3758 int port1, port2;
3759 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003760
Christopher Faulet61cc8522020-04-20 14:54:42 +02003761 if (!*(args[cur_arg+1])) {
3762 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3763 goto error;
3764 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003765
Christopher Faulet61cc8522020-04-20 14:54:42 +02003766 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3767 if (!sk) {
3768 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3769 goto error;
3770 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003771
Christopher Faulet61cc8522020-04-20 14:54:42 +02003772 proto = protocol_by_family(sk->ss_family);
3773 if (!proto || !proto->connect) {
3774 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3775 args[cur_arg]);
3776 goto error;
3777 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003778
Christopher Faulet61cc8522020-04-20 14:54:42 +02003779 if (port1 != port2) {
3780 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3781 args[cur_arg], args[cur_arg+1]);
3782 goto error;
3783 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003784
Christopher Faulet61cc8522020-04-20 14:54:42 +02003785 cur_arg++;
3786 }
3787 else if (strcmp(args[cur_arg], "port") == 0) {
3788 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003789
Christopher Faulet61cc8522020-04-20 14:54:42 +02003790 if (!*(args[cur_arg+1])) {
3791 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3792 goto error;
3793 }
3794 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003795
Christopher Faulet61cc8522020-04-20 14:54:42 +02003796 port = 0;
3797 release_sample_expr(port_expr);
3798 p = args[cur_arg]; end = p + strlen(p);
3799 port = read_uint(&p, end);
3800 if (p != end) {
3801 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003802
Christopher Faulet61cc8522020-04-20 14:54:42 +02003803 px->conf.args.ctx = ARGC_SRV;
3804 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3805 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003806
Christopher Faulet61cc8522020-04-20 14:54:42 +02003807 if (!port_expr) {
3808 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3809 goto error;
3810 }
3811 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3812 memprintf(errmsg, "error detected while parsing port expression : "
3813 " fetch method '%s' extracts information from '%s', "
3814 "none of which is available here.\n",
3815 args[cur_arg], sample_src_names(port_expr->fetch->use));
3816 goto error;
3817 }
3818 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3819 }
3820 else if (port > 65535 || port < 1) {
3821 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3822 args[cur_arg]);
3823 goto error;
3824 }
3825 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003826 else if (strcmp(args[cur_arg], "proto") == 0) {
3827 if (!*(args[cur_arg+1])) {
3828 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3829 goto error;
3830 }
3831 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3832 if (!mux_proto) {
3833 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3834 goto error;
3835 }
3836 cur_arg++;
3837 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003838 else if (strcmp(args[cur_arg], "comment") == 0) {
3839 if (!*(args[cur_arg+1])) {
3840 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3841 goto error;
3842 }
3843 cur_arg++;
3844 free(comment);
3845 comment = strdup(args[cur_arg]);
3846 if (!comment) {
3847 memprintf(errmsg, "out of memory");
3848 goto error;
3849 }
3850 }
3851 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3852 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3853 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3854 conn_opts |= TCPCHK_OPT_SOCKS4;
3855 else if (strcmp(args[cur_arg], "linger") == 0)
3856 conn_opts |= TCPCHK_OPT_LINGER;
3857#ifdef USE_OPENSSL
3858 else if (strcmp(args[cur_arg], "ssl") == 0) {
3859 px->options |= PR_O_TCPCHK_SSL;
3860 conn_opts |= TCPCHK_OPT_SSL;
3861 }
3862 else if (strcmp(args[cur_arg], "sni") == 0) {
3863 if (!*(args[cur_arg+1])) {
3864 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3865 goto error;
3866 }
3867 cur_arg++;
3868 free(sni);
3869 sni = strdup(args[cur_arg]);
3870 if (!sni) {
3871 memprintf(errmsg, "out of memory");
3872 goto error;
3873 }
3874 }
3875 else if (strcmp(args[cur_arg], "alpn") == 0) {
3876#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3877 free(alpn);
3878 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3879 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3880 goto error;
3881 }
3882 cur_arg++;
3883#else
3884 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003885 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003886#endif
3887 }
3888#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003889
Christopher Faulet61cc8522020-04-20 14:54:42 +02003890 else {
3891 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3892#ifdef USE_OPENSSL
3893 ", 'ssl', 'sni', 'alpn'"
3894#endif /* USE_OPENSSL */
3895 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3896 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003897 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003898 }
3899 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003900 }
3901
Christopher Faulet61cc8522020-04-20 14:54:42 +02003902 chk = calloc(1, sizeof(*chk));
3903 if (!chk) {
3904 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003905 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003906 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003907 chk->action = TCPCHK_ACT_CONNECT;
3908 chk->comment = comment;
3909 chk->connect.port = port;
3910 chk->connect.options = conn_opts;
3911 chk->connect.sni = sni;
3912 chk->connect.alpn = alpn;
3913 chk->connect.alpn_len= alpn_len;
3914 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003915 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003916 if (sk)
3917 chk->connect.addr = *sk;
3918 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003919
Christopher Faulet61cc8522020-04-20 14:54:42 +02003920 error:
3921 free(alpn);
3922 free(sni);
3923 free(comment);
3924 release_sample_expr(port_expr);
3925 return NULL;
3926}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003927
Christopher Faulet61cc8522020-04-20 14:54:42 +02003928/* Parses and creates a tcp-check send rule. NULL is returned on error */
3929static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3930 const char *file, int line, char **errmsg)
3931{
3932 struct tcpcheck_rule *chk = NULL;
3933 char *comment = NULL, *data = NULL;
3934 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003935
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003936 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3937 type = TCPCHK_SEND_BINARY_LF;
3938 else if (strcmp(args[cur_arg], "send-binary") == 0)
3939 type = TCPCHK_SEND_BINARY;
3940 else if (strcmp(args[cur_arg], "send-lf") == 0)
3941 type = TCPCHK_SEND_STRING_LF;
3942 else if (strcmp(args[cur_arg], "send") == 0)
3943 type = TCPCHK_SEND_STRING;
3944
Christopher Faulet61cc8522020-04-20 14:54:42 +02003945 if (!*(args[cur_arg+1])) {
3946 memprintf(errmsg, "'%s' expects a %s as argument",
3947 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003948 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003949 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003950
Christopher Faulet61cc8522020-04-20 14:54:42 +02003951 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003952
Christopher Faulet61cc8522020-04-20 14:54:42 +02003953 cur_arg += 2;
3954 while (*(args[cur_arg])) {
3955 if (strcmp(args[cur_arg], "comment") == 0) {
3956 if (!*(args[cur_arg+1])) {
3957 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3958 goto error;
3959 }
3960 cur_arg++;
3961 free(comment);
3962 comment = strdup(args[cur_arg]);
3963 if (!comment) {
3964 memprintf(errmsg, "out of memory");
3965 goto error;
3966 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003967 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003968 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003969 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003970 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003971 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003972 }
3973 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003974 }
3975
Christopher Faulet61cc8522020-04-20 14:54:42 +02003976 chk = calloc(1, sizeof(*chk));
3977 if (!chk) {
3978 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003979 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003980 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003981 chk->action = TCPCHK_ACT_SEND;
3982 chk->comment = comment;
3983 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003984
Christopher Faulet61cc8522020-04-20 14:54:42 +02003985 switch (chk->send.type) {
3986 case TCPCHK_SEND_STRING:
3987 chk->send.data = ist2(strdup(data), strlen(data));
3988 if (!isttest(chk->send.data)) {
3989 memprintf(errmsg, "out of memory");
3990 goto error;
3991 }
3992 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003993 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003994 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003995 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003996 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3997 goto error;
3998 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003999 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004000 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004001 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004002 case TCPCHK_SEND_STRING_LF:
4003 case TCPCHK_SEND_BINARY_LF:
4004 LIST_INIT(&chk->send.fmt);
4005 px->conf.args.ctx = ARGC_SRV;
4006 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4007 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
4008 goto error;
4009 }
4010 break;
4011 case TCPCHK_SEND_HTTP:
4012 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004013 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004014 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004015
Christopher Faulet61cc8522020-04-20 14:54:42 +02004016 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004017
Christopher Faulet61cc8522020-04-20 14:54:42 +02004018 error:
4019 free(chk);
4020 free(comment);
4021 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004022}
4023
Christopher Faulet61cc8522020-04-20 14:54:42 +02004024/* Parses and creates a http-check send rule. NULL is returned on error */
4025static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
4026 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004027{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004028 struct tcpcheck_rule *chk = NULL;
4029 struct tcpcheck_http_hdr *hdr = NULL;
4030 struct http_hdr hdrs[global.tune.max_http_hdr];
4031 char *meth = NULL, *uri = NULL, *vsn = NULL;
4032 char *body = NULL, *comment = NULL;
4033 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004034 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004035
4036 cur_arg++;
4037 while (*(args[cur_arg])) {
4038 if (strcmp(args[cur_arg], "meth") == 0) {
4039 if (!*(args[cur_arg+1])) {
4040 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4041 goto error;
4042 }
4043 cur_arg++;
4044 meth = args[cur_arg];
4045 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004046 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004047 if (!*(args[cur_arg+1])) {
4048 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4049 goto error;
4050 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004051 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4052 if (strcmp(args[cur_arg], "uri-lf") == 0)
4053 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004054 cur_arg++;
4055 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004056 }
Christopher Faulet907701b2020-04-28 09:37:00 +02004057 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004058 if (!*(args[cur_arg+1])) {
4059 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4060 goto error;
4061 }
4062 cur_arg++;
4063 vsn = args[cur_arg];
4064 }
4065 else if (strcmp(args[cur_arg], "hdr") == 0) {
4066 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4067 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4068 goto error;
4069 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004070
4071 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4072 if (host_hdr >= 0) {
4073 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4074 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4075 goto error;
4076 }
4077 host_hdr = i;
4078 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004079 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4080 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4081 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4082 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004083
Christopher Faulet61cc8522020-04-20 14:54:42 +02004084 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4085 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4086 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004087 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004088 cur_arg += 2;
4089 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004090 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004091 if (!*(args[cur_arg+1])) {
4092 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4093 goto error;
4094 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004095 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4096 if (strcmp(args[cur_arg], "body-lf") == 0)
4097 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004098 cur_arg++;
4099 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004100 }
4101 else if (strcmp(args[cur_arg], "comment") == 0) {
4102 if (!*(args[cur_arg+1])) {
4103 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4104 goto error;
4105 }
4106 cur_arg++;
4107 free(comment);
4108 comment = strdup(args[cur_arg]);
4109 if (!comment) {
4110 memprintf(errmsg, "out of memory");
4111 goto error;
4112 }
4113 }
4114 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004115 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004116 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004117 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004118 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004119 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004120 }
4121
Christopher Faulet61cc8522020-04-20 14:54:42 +02004122 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004123
Christopher Faulet61cc8522020-04-20 14:54:42 +02004124 chk = calloc(1, sizeof(*chk));
4125 if (!chk) {
4126 memprintf(errmsg, "out of memory");
4127 goto error;
4128 }
4129 chk->action = TCPCHK_ACT_SEND;
4130 chk->comment = comment; comment = NULL;
4131 chk->send.type = TCPCHK_SEND_HTTP;
4132 chk->send.http.flags = flags;
4133 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004134
Christopher Faulet61cc8522020-04-20 14:54:42 +02004135 if (meth) {
4136 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4137 chk->send.http.meth.str.area = strdup(meth);
4138 chk->send.http.meth.str.data = strlen(meth);
4139 if (!chk->send.http.meth.str.area) {
4140 memprintf(errmsg, "out of memory");
4141 goto error;
4142 }
4143 }
4144 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004145 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4146 LIST_INIT(&chk->send.http.uri_fmt);
4147 px->conf.args.ctx = ARGC_SRV;
4148 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4149 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4150 goto error;
4151 }
4152 }
4153 else {
4154 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4155 if (!isttest(chk->send.http.uri)) {
4156 memprintf(errmsg, "out of memory");
4157 goto error;
4158 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004159 }
4160 }
4161 if (vsn) {
4162 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4163 if (!isttest(chk->send.http.vsn)) {
4164 memprintf(errmsg, "out of memory");
4165 goto error;
4166 }
4167 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004168 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004169 hdr = calloc(1, sizeof(*hdr));
4170 if (!hdr) {
4171 memprintf(errmsg, "out of memory");
4172 goto error;
4173 }
4174 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004175 hdr->name = istdup(hdrs[i].n);
4176 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004177 memprintf(errmsg, "out of memory");
4178 goto error;
4179 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004180
Christopher Fauletb61caf42020-04-21 10:57:42 +02004181 ist0(hdrs[i].v);
4182 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 +02004183 goto error;
4184 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4185 hdr = NULL;
4186 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004187
Christopher Faulet61cc8522020-04-20 14:54:42 +02004188 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004189 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4190 LIST_INIT(&chk->send.http.body_fmt);
4191 px->conf.args.ctx = ARGC_SRV;
4192 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4193 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4194 goto error;
4195 }
4196 }
4197 else {
4198 chk->send.http.body = ist2(strdup(body), strlen(body));
4199 if (!isttest(chk->send.http.body)) {
4200 memprintf(errmsg, "out of memory");
4201 goto error;
4202 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004203 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004204 }
4205
Christopher Faulet61cc8522020-04-20 14:54:42 +02004206 return chk;
4207
4208 error:
4209 free_tcpcheck_http_hdr(hdr);
4210 free_tcpcheck(chk, 0);
4211 free(comment);
4212 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004213}
4214
Christopher Faulet61cc8522020-04-20 14:54:42 +02004215/* Parses and creates a http-check comment rule. NULL is returned on error */
4216static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4217 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004218{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004219 struct tcpcheck_rule *chk = NULL;
4220 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004221
Christopher Faulet61cc8522020-04-20 14:54:42 +02004222 if (!*(args[cur_arg+1])) {
4223 memprintf(errmsg, "expects a string as argument");
4224 goto error;
4225 }
4226 cur_arg++;
4227 comment = strdup(args[cur_arg]);
4228 if (!comment) {
4229 memprintf(errmsg, "out of memory");
4230 goto error;
4231 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004232
Christopher Faulet61cc8522020-04-20 14:54:42 +02004233 chk = calloc(1, sizeof(*chk));
4234 if (!chk) {
4235 memprintf(errmsg, "out of memory");
4236 goto error;
4237 }
4238 chk->action = TCPCHK_ACT_COMMENT;
4239 chk->comment = comment;
4240 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004241
Christopher Faulet61cc8522020-04-20 14:54:42 +02004242 error:
4243 free(comment);
4244 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004245}
4246
Christopher Faulet61cc8522020-04-20 14:54:42 +02004247/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4248 * on error. <proto> is set to the right protocol flags (covered by the
4249 * TCPCHK_RULES_PROTO_CHK mask).
4250 */
4251static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4252 struct list *rules, unsigned int proto,
4253 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004254{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004255 struct tcpcheck_rule *prev_check, *chk = NULL;
4256 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004257 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004258 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004259 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4260 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4261 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004262 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004263 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004264 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004265
Christopher Faulet39708192020-05-05 10:47:36 +02004266 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004267 if (!*(args[cur_arg+1])) {
4268 memprintf(errmsg, "expects at least a matching pattern as arguments");
4269 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004270 }
4271
Christopher Faulet61cc8522020-04-20 14:54:42 +02004272 cur_arg++;
4273 while (*(args[cur_arg])) {
4274 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004275
Christopher Faulet61cc8522020-04-20 14:54:42 +02004276 rescan:
4277 if (strcmp(args[cur_arg], "min-recv") == 0) {
4278 if (in_pattern) {
4279 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4280 goto error;
4281 }
4282 if (!*(args[cur_arg+1])) {
4283 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4284 goto error;
4285 }
4286 /* Use an signed integer here because of chksize */
4287 cur_arg++;
4288 min_recv = atol(args[cur_arg]);
4289 if (min_recv < -1 || min_recv > INT_MAX) {
4290 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4291 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004292 }
4293 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004294 else if (*(args[cur_arg]) == '!') {
4295 in_pattern = 1;
4296 while (*(args[cur_arg]) == '!') {
4297 inverse = !inverse;
4298 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004299 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004300 if (!*(args[cur_arg]))
4301 cur_arg++;
4302 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004303 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004304 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4305 if (type != TCPCHK_EXPECT_UNDEF) {
4306 memprintf(errmsg, "only on pattern expected");
4307 goto error;
4308 }
4309 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004310 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004311 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004312 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004313
Christopher Faulet61cc8522020-04-20 14:54:42 +02004314 if (!*(args[cur_arg+1])) {
4315 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4316 goto error;
4317 }
4318 cur_arg++;
4319 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004320 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004321 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4322 if (proto == TCPCHK_RULES_HTTP_CHK)
4323 goto bad_http_kw;
4324 if (type != TCPCHK_EXPECT_UNDEF) {
4325 memprintf(errmsg, "only on pattern expected");
4326 goto error;
4327 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004328 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004329
Christopher Faulet61cc8522020-04-20 14:54:42 +02004330 if (!*(args[cur_arg+1])) {
4331 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4332 goto error;
4333 }
4334 cur_arg++;
4335 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004336 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004337 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4338 if (type != TCPCHK_EXPECT_UNDEF) {
4339 memprintf(errmsg, "only on pattern expected");
4340 goto error;
4341 }
4342 if (proto != TCPCHK_RULES_HTTP_CHK)
4343 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4344 else {
4345 if (*(args[cur_arg]) != 's')
4346 goto bad_http_kw;
4347 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4348 }
4349
4350 if (!*(args[cur_arg+1])) {
4351 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4352 goto error;
4353 }
4354 cur_arg++;
4355 pattern = args[cur_arg];
4356 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004357 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4358 if (proto != TCPCHK_RULES_HTTP_CHK)
4359 goto bad_tcp_kw;
4360 if (type != TCPCHK_EXPECT_UNDEF) {
4361 memprintf(errmsg, "only on pattern expected");
4362 goto error;
4363 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004364 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004365
Christopher Faulet61cc8522020-04-20 14:54:42 +02004366 if (!*(args[cur_arg+1])) {
4367 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4368 goto error;
4369 }
4370 cur_arg++;
4371 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004372 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004373 else if (strcmp(args[cur_arg], "custom") == 0) {
4374 if (in_pattern) {
4375 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4376 goto error;
4377 }
4378 if (type != TCPCHK_EXPECT_UNDEF) {
4379 memprintf(errmsg, "only on pattern expected");
4380 goto error;
4381 }
4382 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004383 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004384 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004385 int orig_arg = cur_arg;
4386
4387 if (proto != TCPCHK_RULES_HTTP_CHK)
4388 goto bad_tcp_kw;
4389 if (type != TCPCHK_EXPECT_UNDEF) {
4390 memprintf(errmsg, "only on pattern expected");
4391 goto error;
4392 }
4393 type = TCPCHK_EXPECT_HTTP_HEADER;
4394
Christopher Fauletb5594262020-05-05 20:23:13 +02004395 if (strcmp(args[cur_arg], "fhdr") == 0)
4396 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4397
Christopher Faulet39708192020-05-05 10:47:36 +02004398 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004399 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4400 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4401 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004402 args[orig_arg]);
4403 goto error;
4404 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004405
4406 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4407 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4408
Christopher Faulet39708192020-05-05 10:47:36 +02004409 cur_arg += 2;
4410 if (strcmp(args[cur_arg], "-m") == 0) {
4411 if (!*(args[cur_arg+1])) {
4412 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4413 args[orig_arg], args[cur_arg]);
4414 goto error;
4415 }
4416 if (strcmp(args[cur_arg+1], "str") == 0)
4417 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4418 else if (strcmp(args[cur_arg+1], "beg") == 0)
4419 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4420 else if (strcmp(args[cur_arg+1], "end") == 0)
4421 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4422 else if (strcmp(args[cur_arg+1], "sub") == 0)
4423 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004424 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4425 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4426 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4427 args[orig_arg]);
4428 goto error;
4429 }
Christopher Faulet39708192020-05-05 10:47:36 +02004430 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004431 }
Christopher Faulet39708192020-05-05 10:47:36 +02004432 else {
4433 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4434 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4435 goto error;
4436 }
4437 cur_arg += 2;
4438 }
4439 else
4440 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4441 npat = args[cur_arg];
4442
Christopher Fauletb5594262020-05-05 20:23:13 +02004443 if (!*(args[cur_arg+1]) ||
4444 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004445 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4446 goto next;
4447 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004448 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4449 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004450
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004451 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004452 if (strcmp(args[cur_arg+2], "-m") == 0) {
4453 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004454 if (!*(args[cur_arg+1])) {
4455 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4456 args[orig_arg], args[cur_arg]);
4457 goto error;
4458 }
4459 if (strcmp(args[cur_arg+1], "str") == 0)
4460 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4461 else if (strcmp(args[cur_arg+1], "beg") == 0)
4462 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4463 else if (strcmp(args[cur_arg+1], "end") == 0)
4464 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4465 else if (strcmp(args[cur_arg+1], "sub") == 0)
4466 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004467 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4468 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4469 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4470 args[orig_arg]);
4471 goto error;
4472 }
Christopher Faulet39708192020-05-05 10:47:36 +02004473 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004474 }
Christopher Faulet39708192020-05-05 10:47:36 +02004475 else {
4476 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4477 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4478 goto error;
4479 }
Christopher Faulet39708192020-05-05 10:47:36 +02004480 }
4481 else
4482 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004483
Christopher Fauletb5594262020-05-05 20:23:13 +02004484 if (!*(args[cur_arg+2])) {
4485 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4486 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004487 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004488 vpat = args[cur_arg+2];
4489 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004490 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004491 else if (strcmp(args[cur_arg], "comment") == 0) {
4492 if (in_pattern) {
4493 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4494 goto error;
4495 }
4496 if (!*(args[cur_arg+1])) {
4497 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4498 goto error;
4499 }
4500 cur_arg++;
4501 free(comment);
4502 comment = strdup(args[cur_arg]);
4503 if (!comment) {
4504 memprintf(errmsg, "out of memory");
4505 goto error;
4506 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004507 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004508 else if (strcmp(args[cur_arg], "on-success") == 0) {
4509 if (in_pattern) {
4510 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4511 goto error;
4512 }
4513 if (!*(args[cur_arg+1])) {
4514 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4515 goto error;
4516 }
4517 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004518 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004519 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004520 else if (strcmp(args[cur_arg], "on-error") == 0) {
4521 if (in_pattern) {
4522 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4523 goto error;
4524 }
4525 if (!*(args[cur_arg+1])) {
4526 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4527 goto error;
4528 }
4529 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004530 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004531 }
4532 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4533 if (in_pattern) {
4534 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4535 goto error;
4536 }
4537 if (!*(args[cur_arg+1])) {
4538 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4539 goto error;
4540 }
4541 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4542 ok_st = HCHK_STATUS_L7OKD;
4543 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4544 ok_st = HCHK_STATUS_L7OKCD;
4545 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4546 ok_st = HCHK_STATUS_L6OK;
4547 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4548 ok_st = HCHK_STATUS_L4OK;
4549 else {
4550 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4551 args[cur_arg], args[cur_arg+1]);
4552 goto error;
4553 }
4554 cur_arg++;
4555 }
4556 else if (strcmp(args[cur_arg], "error-status") == 0) {
4557 if (in_pattern) {
4558 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4559 goto error;
4560 }
4561 if (!*(args[cur_arg+1])) {
4562 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4563 goto error;
4564 }
4565 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4566 err_st = HCHK_STATUS_L7RSP;
4567 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4568 err_st = HCHK_STATUS_L7STS;
4569 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4570 err_st = HCHK_STATUS_L6RSP;
4571 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4572 err_st = HCHK_STATUS_L4CON;
4573 else {
4574 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4575 args[cur_arg], args[cur_arg+1]);
4576 goto error;
4577 }
4578 cur_arg++;
4579 }
4580 else if (strcmp(args[cur_arg], "status-code") == 0) {
4581 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004582
Christopher Faulet61cc8522020-04-20 14:54:42 +02004583 if (in_pattern) {
4584 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4585 goto error;
4586 }
4587 if (!*(args[cur_arg+1])) {
4588 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4589 goto error;
4590 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004591
Christopher Faulet61cc8522020-04-20 14:54:42 +02004592 cur_arg++;
4593 release_sample_expr(status_expr);
4594 px->conf.args.ctx = ARGC_SRV;
4595 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4596 file, line, errmsg, &px->conf.args, NULL);
4597 if (!status_expr) {
4598 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4599 goto error;
4600 }
4601 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4602 memprintf(errmsg, "error detected while parsing status-code expression : "
4603 " fetch method '%s' extracts information from '%s', "
4604 "none of which is available here.\n",
4605 args[cur_arg], sample_src_names(status_expr->fetch->use));
4606 goto error;
4607 }
4608 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4609 }
4610 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4611 if (in_pattern) {
4612 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4613 goto error;
4614 }
4615 if (!*(args[cur_arg+1])) {
4616 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4617 goto error;
4618 }
4619 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4620 tout_st = HCHK_STATUS_L7TOUT;
4621 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4622 tout_st = HCHK_STATUS_L6TOUT;
4623 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4624 tout_st = HCHK_STATUS_L4TOUT;
4625 else {
4626 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4627 args[cur_arg], args[cur_arg+1]);
4628 goto error;
4629 }
4630 cur_arg++;
4631 }
4632 else {
4633 if (proto == TCPCHK_RULES_HTTP_CHK) {
4634 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004635 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004636 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004637 }
4638 else {
4639 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004640 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4641 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004642 }
4643 goto error;
4644 }
Christopher Faulet39708192020-05-05 10:47:36 +02004645 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004646 cur_arg++;
4647 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004648
Christopher Faulet61cc8522020-04-20 14:54:42 +02004649 chk = calloc(1, sizeof(*chk));
4650 if (!chk) {
4651 memprintf(errmsg, "out of memory");
4652 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004653 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004654 chk->action = TCPCHK_ACT_EXPECT;
4655 LIST_INIT(&chk->expect.onerror_fmt);
4656 LIST_INIT(&chk->expect.onsuccess_fmt);
4657 chk->comment = comment; comment = NULL;
4658 chk->expect.type = type;
4659 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004660 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004661 chk->expect.ok_status = ok_st;
4662 chk->expect.err_status = err_st;
4663 chk->expect.tout_status = tout_st;
4664 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004665
Christopher Faulet61cc8522020-04-20 14:54:42 +02004666 if (on_success_msg) {
4667 px->conf.args.ctx = ARGC_SRV;
4668 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4669 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4670 goto error;
4671 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004672 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004673 if (on_error_msg) {
4674 px->conf.args.ctx = ARGC_SRV;
4675 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4676 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4677 goto error;
4678 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004679 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004680
Christopher Faulet61cc8522020-04-20 14:54:42 +02004681 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004682 case TCPCHK_EXPECT_HTTP_STATUS: {
4683 const char *p = pattern;
4684 unsigned int c1,c2;
4685
4686 chk->expect.codes.codes = NULL;
4687 chk->expect.codes.num = 0;
4688 while (1) {
4689 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4690 if (*p == '-') {
4691 p++;
4692 c2 = read_uint(&p, pattern + strlen(pattern));
4693 }
4694 if (c1 > c2) {
4695 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4696 goto error;
4697 }
4698
4699 chk->expect.codes.num++;
4700 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4701 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4702 if (!chk->expect.codes.codes) {
4703 memprintf(errmsg, "out of memory");
4704 goto error;
4705 }
4706 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4707 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4708
4709 if (*p == '\0')
4710 break;
4711 if (*p != ',') {
4712 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4713 goto error;
4714 }
4715 p++;
4716 }
4717 break;
4718 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004719 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004720 case TCPCHK_EXPECT_HTTP_BODY:
4721 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004722 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004723 memprintf(errmsg, "out of memory");
4724 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004725 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004726 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004727 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004728 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004729
4730 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004731 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4732 goto error;
4733 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004734 chk->expect.data.len = len;
4735 break;
4736 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004737 case TCPCHK_EXPECT_STRING_REGEX:
4738 case TCPCHK_EXPECT_BINARY_REGEX:
4739 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4740 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004741 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004742 if (!chk->expect.regex)
4743 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004744 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004745
4746 case TCPCHK_EXPECT_STRING_LF:
4747 case TCPCHK_EXPECT_BINARY_LF:
4748 case TCPCHK_EXPECT_HTTP_BODY_LF:
4749 LIST_INIT(&chk->expect.fmt);
4750 px->conf.args.ctx = ARGC_SRV;
4751 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4752 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4753 goto error;
4754 }
4755 break;
4756
Christopher Faulet39708192020-05-05 10:47:36 +02004757 case TCPCHK_EXPECT_HTTP_HEADER:
4758 if (!npat) {
4759 memprintf(errmsg, "unexpected error, undefined header name pattern");
4760 goto error;
4761 }
4762 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4763 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4764 if (!chk->expect.hdr.name_re)
4765 goto error;
4766 }
4767 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4768 px->conf.args.ctx = ARGC_SRV;
4769 LIST_INIT(&chk->expect.hdr.name_fmt);
4770 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4771 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4772 goto error;
4773 }
4774 }
4775 else {
4776 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4777 if (!isttest(chk->expect.hdr.name)) {
4778 memprintf(errmsg, "out of memory");
4779 goto error;
4780 }
4781 }
4782
4783 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4784 chk->expect.hdr.value = IST_NULL;
4785 break;
4786 }
4787
4788 if (!vpat) {
4789 memprintf(errmsg, "unexpected error, undefined header value pattern");
4790 goto error;
4791 }
4792 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4793 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4794 if (!chk->expect.hdr.value_re)
4795 goto error;
4796 }
4797 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4798 px->conf.args.ctx = ARGC_SRV;
4799 LIST_INIT(&chk->expect.hdr.value_fmt);
4800 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4801 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4802 goto error;
4803 }
4804 }
4805 else {
4806 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4807 if (!isttest(chk->expect.hdr.value)) {
4808 memprintf(errmsg, "out of memory");
4809 goto error;
4810 }
4811 }
4812
Christopher Faulet61cc8522020-04-20 14:54:42 +02004813 break;
4814 case TCPCHK_EXPECT_CUSTOM:
4815 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4816 break;
4817 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004818 memprintf(errmsg, "pattern not found");
4819 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004820 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004821
Christopher Faulet61cc8522020-04-20 14:54:42 +02004822 /* All tcp-check expect points back to the first inverse expect rule in
4823 * a chain of one or more expect rule, potentially itself.
4824 */
4825 chk->expect.head = chk;
4826 list_for_each_entry_rev(prev_check, rules, list) {
4827 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4828 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4829 chk->expect.head = prev_check;
4830 continue;
4831 }
4832 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4833 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004834 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004835 return chk;
4836
4837 error:
4838 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004839 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004840 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004841 return NULL;
4842}
4843
Christopher Faulet61cc8522020-04-20 14:54:42 +02004844/* Overwrites fields of the old http send rule with those of the new one. When
4845 * replaced, old values are freed and replaced by the new ones. New values are
4846 * not copied but transferred. At the end <new> should be empty and can be
4847 * safely released. This function never fails.
4848 */
4849static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004850{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004851 struct logformat_node *lf, *lfb;
4852 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004853
Christopher Faulet404f9192020-04-09 23:13:54 +02004854
Christopher Faulet61cc8522020-04-20 14:54:42 +02004855 if (new->send.http.meth.str.area) {
4856 free(old->send.http.meth.str.area);
4857 old->send.http.meth.meth = new->send.http.meth.meth;
4858 old->send.http.meth.str.area = new->send.http.meth.str.area;
4859 old->send.http.meth.str.data = new->send.http.meth.str.data;
4860 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004861 }
4862
Christopher Faulet61cc8522020-04-20 14:54:42 +02004863 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4864 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004865 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004866 else
4867 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4868 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4869 old->send.http.uri = new->send.http.uri;
4870 new->send.http.uri = IST_NULL;
4871 }
4872 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4873 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004874 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004875 else
4876 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4877 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4878 LIST_INIT(&old->send.http.uri_fmt);
4879 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4880 LIST_DEL(&lf->list);
4881 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4882 }
4883 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004884
Christopher Faulet61cc8522020-04-20 14:54:42 +02004885 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004886 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004887 old->send.http.vsn = new->send.http.vsn;
4888 new->send.http.vsn = IST_NULL;
4889 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004890
Christopher Faulet61cc8522020-04-20 14:54:42 +02004891 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4892 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4893 LIST_DEL(&hdr->list);
4894 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004895 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004896
4897 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4898 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004899 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004900 else
4901 free_tcpcheck_fmt(&old->send.http.body_fmt);
4902 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4903 old->send.http.body = new->send.http.body;
4904 new->send.http.body = IST_NULL;
4905 }
4906 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4907 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004908 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004909 else
4910 free_tcpcheck_fmt(&old->send.http.body_fmt);
4911 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4912 LIST_INIT(&old->send.http.body_fmt);
4913 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4914 LIST_DEL(&lf->list);
4915 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4916 }
4917 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004918}
4919
Christopher Faulet61cc8522020-04-20 14:54:42 +02004920/* Internal function used to add an http-check rule in a list during the config
4921 * parsing step. Depending on its type, and the previously inserted rules, a
4922 * specific action may be performed or an error may be reported. This functions
4923 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4924 * message.
4925 */
4926static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004927{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004928 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004929
Christopher Faulet61cc8522020-04-20 14:54:42 +02004930 /* the implicit send rule coming from an "option httpchk" line must be
4931 * merged with the first explici http-check send rule, if
4932 * any. Depdending the declaration order some tests are required.
4933 *
4934 * Some tests is also required for other kinds of http-check rules to be
4935 * sure the ruleset remains valid.
4936 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004937
Christopher Faulet61cc8522020-04-20 14:54:42 +02004938 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004939 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004940 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4941 * following tests are performed :
4942 *
4943 * 1- If there is no such rule or if it is not a send rule, the implicit send
4944 * rule is pushed in front of the ruleset
4945 *
4946 * 2- If it is another implicit send rule, it is replaced with the new one.
4947 *
4948 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004949 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004950 * new send rule (the implicit one).
4951 */
4952 r = get_first_tcpcheck_rule(rules);
4953 if (r && r->action == TCPCHK_ACT_CONNECT)
4954 r = get_next_tcpcheck_rule(rules, r);
4955 if (!r || r->action != TCPCHK_ACT_SEND)
4956 LIST_ADD(rules->list, &chk->list);
4957 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4958 LIST_DEL(&r->list);
4959 free_tcpcheck(r, 0);
4960 LIST_ADD(rules->list, &chk->list);
4961 }
4962 else {
4963 tcpcheck_overwrite_send_http_rule(r, chk);
4964 free_tcpcheck(chk, 0);
4965 }
4966 }
4967 else {
4968 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4969 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4970 * with an existing implicit send rule, if any. At the end, if there is no error,
4971 * the rule is appended to the list.
4972 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004973
Christopher Faulet61cc8522020-04-20 14:54:42 +02004974 r = get_last_tcpcheck_rule(rules);
4975 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4976 /* no error */;
4977 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4978 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4979 chk->index+1);
4980 return 0;
4981 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004982 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004983 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4984 chk->index+1);
4985 return 0;
4986 }
4987 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4988 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4989 chk->index+1);
4990 return 0;
4991 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004992
Christopher Faulet61cc8522020-04-20 14:54:42 +02004993 if (chk->action == TCPCHK_ACT_SEND) {
4994 r = get_first_tcpcheck_rule(rules);
4995 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4996 tcpcheck_overwrite_send_http_rule(r, chk);
4997 free_tcpcheck(chk, 0);
4998 LIST_DEL(&r->list);
4999 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
5000 chk = r;
5001 }
5002 }
5003 LIST_ADDQ(rules->list, &chk->list);
5004 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02005005 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02005006}
5007
Christopher Faulet61cc8522020-04-20 14:54:42 +02005008/**************************************************************************/
5009/************************** Init/deinit checks ****************************/
5010/**************************************************************************/
5011static const char *init_check(struct check *check, int type)
5012{
5013 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02005014
Christopher Faulet61cc8522020-04-20 14:54:42 +02005015 b_reset(&check->bi); check->bi.size = global.tune.chksize;
5016 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02005017
Christopher Faulet61cc8522020-04-20 14:54:42 +02005018 check->bi.area = calloc(check->bi.size, sizeof(char));
5019 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02005020
Christopher Faulet61cc8522020-04-20 14:54:42 +02005021 if (!check->bi.area || !check->bo.area)
5022 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005023
Christopher Faulet61cc8522020-04-20 14:54:42 +02005024 check->wait_list.tasklet = tasklet_new();
5025 if (!check->wait_list.tasklet)
5026 return "out of memory while allocating check tasklet";
5027 check->wait_list.events = 0;
5028 check->wait_list.tasklet->process = event_srv_chk_io;
5029 check->wait_list.tasklet->context = check;
5030 return NULL;
5031}
5032
5033void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005034{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005035 task_destroy(check->task);
5036 if (check->wait_list.tasklet)
5037 tasklet_free(check->wait_list.tasklet);
5038
5039 free(check->bi.area);
5040 free(check->bo.area);
5041 if (check->cs) {
5042 free(check->cs->conn);
5043 check->cs->conn = NULL;
5044 cs_free(check->cs);
5045 check->cs = NULL;
5046 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005047}
5048
Christopher Faulet61cc8522020-04-20 14:54:42 +02005049/* manages a server health-check. Returns the time the task accepts to wait, or
5050 * TIME_ETERNITY for infinity.
5051 */
5052static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005053{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005054 struct check *check = context;
5055
5056 if (check->type == PR_O2_EXT_CHK)
5057 return process_chk_proc(t, context, state);
5058 return process_chk_conn(t, context, state);
5059
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005060}
5061
Christopher Faulet61cc8522020-04-20 14:54:42 +02005062
5063static int start_check_task(struct check *check, int mininter,
5064 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005065{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005066 struct task *t;
5067 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005068
Christopher Faulet61cc8522020-04-20 14:54:42 +02005069 if (check->type == PR_O2_EXT_CHK)
5070 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005071
Christopher Faulet61cc8522020-04-20 14:54:42 +02005072 /* task for the check */
5073 if ((t = task_new(thread_mask)) == NULL) {
5074 ha_alert("Starting [%s:%s] check: out of memory.\n",
5075 check->server->proxy->id, check->server->id);
5076 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005077 }
5078
Christopher Faulet61cc8522020-04-20 14:54:42 +02005079 check->task = t;
5080 t->process = process_chk;
5081 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005082
Christopher Faulet61cc8522020-04-20 14:54:42 +02005083 if (mininter < srv_getinter(check))
5084 mininter = srv_getinter(check);
5085
5086 if (global.max_spread_checks && mininter > global.max_spread_checks)
5087 mininter = global.max_spread_checks;
5088
5089 /* check this every ms */
5090 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5091 check->start = now;
5092 task_queue(t);
5093
5094 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005095}
5096
Christopher Faulet61cc8522020-04-20 14:54:42 +02005097/* updates the server's weight during a warmup stage. Once the final weight is
5098 * reached, the task automatically stops. Note that any server status change
5099 * must have updated s->last_change accordingly.
5100 */
5101static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005102{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005103 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005104
Christopher Faulet61cc8522020-04-20 14:54:42 +02005105 /* by default, plan on stopping the task */
5106 t->expire = TICK_ETERNITY;
5107 if ((s->next_admin & SRV_ADMF_MAINT) ||
5108 (s->next_state != SRV_ST_STARTING))
5109 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005110
Christopher Faulet61cc8522020-04-20 14:54:42 +02005111 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005112
Christopher Faulet61cc8522020-04-20 14:54:42 +02005113 /* recalculate the weights and update the state */
5114 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005115
Christopher Faulet61cc8522020-04-20 14:54:42 +02005116 /* probably that we can refill this server with a bit more connections */
5117 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005118
Christopher Faulet61cc8522020-04-20 14:54:42 +02005119 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005120
Christopher Faulet61cc8522020-04-20 14:54:42 +02005121 /* get back there in 1 second or 1/20th of the slowstart interval,
5122 * whichever is greater, resulting in small 5% steps.
5123 */
5124 if (s->next_state == SRV_ST_STARTING)
5125 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5126 return t;
5127}
5128
5129/*
5130 * Start health-check.
5131 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5132 */
5133static int start_checks()
5134{
5135
5136 struct proxy *px;
5137 struct server *s;
5138 struct task *t;
5139 int nbcheck=0, mininter=0, srvpos=0;
5140
5141 /* 0- init the dummy frontend used to create all checks sessions */
5142 init_new_proxy(&checks_fe);
5143 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5144 checks_fe.mode = PR_MODE_TCP;
5145 checks_fe.maxconn = 0;
5146 checks_fe.conn_retries = CONN_RETRIES;
5147 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5148 checks_fe.timeout.client = TICK_ETERNITY;
5149
5150 /* 1- count the checkers to run simultaneously.
5151 * We also determine the minimum interval among all of those which
5152 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5153 * will be used to spread their start-up date. Those which have
5154 * a shorter interval will start independently and will not dictate
5155 * too short an interval for all others.
5156 */
5157 for (px = proxies_list; px; px = px->next) {
5158 for (s = px->srv; s; s = s->next) {
5159 if (s->slowstart) {
5160 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5161 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5162 return ERR_ALERT | ERR_FATAL;
5163 }
5164 /* We need a warmup task that will be called when the server
5165 * state switches from down to up.
5166 */
5167 s->warmup = t;
5168 t->process = server_warmup;
5169 t->context = s;
5170 /* server can be in this state only because of */
5171 if (s->next_state == SRV_ST_STARTING)
5172 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 +02005173 }
5174
Christopher Faulet61cc8522020-04-20 14:54:42 +02005175 if (s->check.state & CHK_ST_CONFIGURED) {
5176 nbcheck++;
5177 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5178 (!mininter || mininter > srv_getinter(&s->check)))
5179 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005180 }
5181
Christopher Faulet61cc8522020-04-20 14:54:42 +02005182 if (s->agent.state & CHK_ST_CONFIGURED) {
5183 nbcheck++;
5184 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5185 (!mininter || mininter > srv_getinter(&s->agent)))
5186 mininter = srv_getinter(&s->agent);
5187 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005188 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005189 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005190
Christopher Faulet61cc8522020-04-20 14:54:42 +02005191 if (!nbcheck)
5192 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005193
Christopher Faulet61cc8522020-04-20 14:54:42 +02005194 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005195
Christopher Faulet61cc8522020-04-20 14:54:42 +02005196 /*
5197 * 2- start them as far as possible from each others. For this, we will
5198 * start them after their interval set to the min interval divided by
5199 * the number of servers, weighted by the server's position in the list.
5200 */
5201 for (px = proxies_list; px; px = px->next) {
5202 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5203 if (init_pid_list()) {
5204 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5205 return ERR_ALERT | ERR_FATAL;
5206 }
5207 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005208
Christopher Faulet61cc8522020-04-20 14:54:42 +02005209 for (s = px->srv; s; s = s->next) {
5210 /* A task for the main check */
5211 if (s->check.state & CHK_ST_CONFIGURED) {
5212 if (s->check.type == PR_O2_EXT_CHK) {
5213 if (!prepare_external_check(&s->check))
5214 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005215 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005216 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5217 return ERR_ALERT | ERR_FATAL;
5218 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005219 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005220
Christopher Faulet61cc8522020-04-20 14:54:42 +02005221 /* A task for a auxiliary agent check */
5222 if (s->agent.state & CHK_ST_CONFIGURED) {
5223 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5224 return ERR_ALERT | ERR_FATAL;
5225 }
5226 srvpos++;
5227 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005228 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005229 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005230 return 0;
5231}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005232
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005233
Christopher Faulet61cc8522020-04-20 14:54:42 +02005234/*
5235 * Return value:
5236 * the port to be used for the health check
5237 * 0 in case no port could be found for the check
5238 */
5239static int srv_check_healthcheck_port(struct check *chk)
5240{
5241 int i = 0;
5242 struct server *srv = NULL;
5243
5244 srv = chk->server;
5245
5246 /* by default, we use the health check port ocnfigured */
5247 if (chk->port > 0)
5248 return chk->port;
5249
5250 /* try to get the port from check_core.addr if check.port not set */
5251 i = get_host_port(&chk->addr);
5252 if (i > 0)
5253 return i;
5254
5255 /* try to get the port from server address */
5256 /* prevent MAPPORTS from working at this point, since checks could
5257 * not be performed in such case (MAPPORTS impose a relative ports
5258 * based on live traffic)
5259 */
5260 if (srv->flags & SRV_F_MAPPORTS)
5261 return 0;
5262
5263 i = srv->svc_port; /* by default */
5264 if (i > 0)
5265 return i;
5266
5267 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005268}
5269
Christopher Faulet61cc8522020-04-20 14:54:42 +02005270/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5271 * if an error occurred.
5272 */
5273static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005274{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005275 const char *err;
5276 struct tcpcheck_rule *r;
5277 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005278
Christopher Faulet61cc8522020-04-20 14:54:42 +02005279 if (!srv->do_check)
5280 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005281
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005282
Christopher Faulet61cc8522020-04-20 14:54:42 +02005283 /* If neither a port nor an addr was specified and no check transport
5284 * layer is forced, then the transport layer used by the checks is the
5285 * same as for the production traffic. Otherwise we use raw_sock by
5286 * default, unless one is specified.
5287 */
5288 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5289 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5290 srv->check.use_ssl = srv->use_ssl;
5291 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005292 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005293 else if (srv->check.use_ssl == 1)
5294 srv->check.xprt = xprt_get(XPRT_SSL);
5295 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005296 }
5297
Christopher Faulet12882cf2020-04-23 15:50:18 +02005298 /* Inherit the mux protocol from the server if not already defined for
5299 * the check
5300 */
5301 if (srv->mux_proto && !srv->check.mux_proto)
5302 srv->check.mux_proto = srv->mux_proto;
5303
Christopher Faulet61cc8522020-04-20 14:54:42 +02005304 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005305
Christopher Faulet61cc8522020-04-20 14:54:42 +02005306 /* We need at least a service port, a check port or the first tcp-check
5307 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5308 */
5309 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5310 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5311 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005312
Christopher Faulet61cc8522020-04-20 14:54:42 +02005313 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5314 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5315 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5316 ret |= ERR_ALERT | ERR_ABORT;
5317 goto out;
5318 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005319
Christopher Faulet61cc8522020-04-20 14:54:42 +02005320 /* search the first action (connect / send / expect) in the list */
5321 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5322 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5323 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5324 "nor tcp_check rule 'connect' with port information.\n",
5325 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5326 ret |= ERR_ALERT | ERR_ABORT;
5327 goto out;
5328 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005329
Christopher Faulet61cc8522020-04-20 14:54:42 +02005330 /* scan the tcp-check ruleset to ensure a port has been configured */
5331 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5332 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5333 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5334 "and a tcp_check rule 'connect' with no port information.\n",
5335 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5336 ret |= ERR_ALERT | ERR_ABORT;
5337 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005338 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005339 }
5340
Christopher Faulet61cc8522020-04-20 14:54:42 +02005341 init:
5342 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5343 struct tcpcheck_ruleset *rs = NULL;
5344 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5345 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005346
Christopher Faulet61cc8522020-04-20 14:54:42 +02005347 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5348 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005349
Christopher Faulet61cc8522020-04-20 14:54:42 +02005350 rs = find_tcpcheck_ruleset("*tcp-check");
5351 if (!rs) {
5352 rs = create_tcpcheck_ruleset("*tcp-check");
5353 if (rs == NULL) {
5354 ha_alert("config: %s '%s': out of memory.\n",
5355 proxy_type_str(srv->proxy), srv->proxy->id);
5356 ret |= ERR_ALERT | ERR_FATAL;
5357 goto out;
5358 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005359 }
5360
Christopher Faulet61cc8522020-04-20 14:54:42 +02005361 free_tcpcheck_vars(&rules->preset_vars);
5362 rules->list = &rs->rules;
5363 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005364 }
5365
Christopher Faulet61cc8522020-04-20 14:54:42 +02005366 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5367 if (err) {
5368 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5369 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5370 ret |= ERR_ALERT | ERR_ABORT;
5371 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005372 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005373 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5374 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005375
Christopher Faulet61cc8522020-04-20 14:54:42 +02005376 out:
5377 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005378}
5379
Christopher Faulet61cc8522020-04-20 14:54:42 +02005380/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5381 * if an error occurred.
5382 */
5383static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005384{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005385 struct tcpcheck_rule *chk;
5386 const char *err;
5387 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005388
Christopher Faulet61cc8522020-04-20 14:54:42 +02005389 if (!srv->do_agent)
5390 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005391
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005392 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005393 * implicit one is inserted before all others.
5394 */
5395 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5396 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5397 chk = calloc(1, sizeof(*chk));
5398 if (!chk) {
5399 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5400 " to agent-check for server '%s' (out of memory).\n",
5401 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5402 ret |= ERR_ALERT | ERR_FATAL;
5403 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005404 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005405 chk->action = TCPCHK_ACT_CONNECT;
5406 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5407 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005408 }
5409
Christopher Faulete5870d82020-04-15 11:32:03 +02005410
Christopher Faulet61cc8522020-04-20 14:54:42 +02005411 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5412 if (err) {
5413 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5414 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5415 ret |= ERR_ALERT | ERR_ABORT;
5416 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005417 }
5418
Christopher Faulet61cc8522020-04-20 14:54:42 +02005419 if (!srv->agent.inter)
5420 srv->agent.inter = srv->check.inter;
5421
5422 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5423 global.maxsock++;
5424
5425 out:
5426 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005427}
5428
Christopher Faulet61cc8522020-04-20 14:54:42 +02005429/* Check tcp-check health-check configuration for the proxy <px>. */
5430static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005431{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005432 struct tcpcheck_rule *chk, *back;
5433 char *comment = NULL, *errmsg = NULL;
5434 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5435 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005436
Christopher Faulet61cc8522020-04-20 14:54:42 +02005437 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5438 deinit_proxy_tcpcheck(px);
5439 goto out;
5440 }
5441
5442 free(px->check_command);
5443 free(px->check_path);
5444 px->check_command = px->check_path = NULL;
5445
5446 if (!px->tcpcheck_rules.list) {
5447 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5448 ret |= ERR_ALERT | ERR_FATAL;
5449 goto out;
5450 }
5451
5452 /* HTTP ruleset only : */
5453 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5454 struct tcpcheck_rule *next;
5455
5456 /* move remaining implicit send rule from "option httpchk" line to the right place.
5457 * If such rule exists, it must be the first one. In this case, the rule is moved
5458 * after the first connect rule, if any. Otherwise, nothing is done.
5459 */
5460 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5461 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5462 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5463 if (next && next->action == TCPCHK_ACT_CONNECT) {
5464 LIST_DEL(&chk->list);
5465 LIST_ADD(&next->list, &chk->list);
5466 chk->index = next->index;
5467 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005468 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005469
5470 /* add implicit expect rule if the last one is a send. It is inherited from previous
5471 * versions where the http expect rule was optional. Now it is possible to chained
5472 * send/expect rules but the last expect may still be implicit.
5473 */
5474 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5475 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005476 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005477 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5478 px->conf.file, px->conf.line, &errmsg);
5479 if (!next) {
5480 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5481 "(%s).\n", px->id, errmsg);
5482 free(errmsg);
5483 ret |= ERR_ALERT | ERR_FATAL;
5484 goto out;
5485 }
5486 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5487 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005488 }
5489 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005490
5491 /* For all ruleset: */
5492
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005493 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005494 * implicit one is inserted before all others.
5495 */
5496 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5497 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5498 chk = calloc(1, sizeof(*chk));
5499 if (!chk) {
5500 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5501 "(out of memory).\n", px->id);
5502 ret |= ERR_ALERT | ERR_FATAL;
5503 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005504 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005505 chk->action = TCPCHK_ACT_CONNECT;
5506 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5507 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5508 }
5509
5510 /* Remove all comment rules. To do so, when a such rule is found, the
5511 * comment is assigned to the following rule(s).
5512 */
5513 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5514 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5515 free(comment);
5516 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005517 }
5518
Christopher Faulet61cc8522020-04-20 14:54:42 +02005519 prev_action = chk->action;
5520 switch (chk->action) {
5521 case TCPCHK_ACT_COMMENT:
5522 free(comment);
5523 comment = chk->comment;
5524 LIST_DEL(&chk->list);
5525 free(chk);
5526 break;
5527 case TCPCHK_ACT_CONNECT:
5528 if (!chk->comment && comment)
5529 chk->comment = strdup(comment);
5530 /* fall though */
5531 case TCPCHK_ACT_ACTION_KW:
5532 free(comment);
5533 comment = NULL;
5534 break;
5535 case TCPCHK_ACT_SEND:
5536 case TCPCHK_ACT_EXPECT:
5537 if (!chk->comment && comment)
5538 chk->comment = strdup(comment);
5539 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005540 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005541 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005542 free(comment);
5543 comment = NULL;
5544
5545 out:
5546 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005547}
5548
Christopher Faulet61cc8522020-04-20 14:54:42 +02005549void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005550{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005551 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5552 px->tcpcheck_rules.flags = 0;
5553 px->tcpcheck_rules.list = NULL;
5554}
Christopher Faulete5870d82020-04-15 11:32:03 +02005555
Christopher Faulet61cc8522020-04-20 14:54:42 +02005556static void deinit_srv_check(struct server *srv)
5557{
5558 if (srv->check.state & CHK_ST_CONFIGURED)
5559 free_check(&srv->check);
5560 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5561 srv->do_check = 0;
5562}
Christopher Faulete5870d82020-04-15 11:32:03 +02005563
Christopher Faulet61cc8522020-04-20 14:54:42 +02005564
5565static void deinit_srv_agent_check(struct server *srv)
5566{
5567 if (srv->agent.tcpcheck_rules) {
5568 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5569 free(srv->agent.tcpcheck_rules);
5570 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005571 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005572
Christopher Faulet61cc8522020-04-20 14:54:42 +02005573 if (srv->agent.state & CHK_ST_CONFIGURED)
5574 free_check(&srv->agent);
5575
5576 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5577 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005578}
5579
Christopher Faulet61cc8522020-04-20 14:54:42 +02005580static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005581{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005582 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005583 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005584 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005585
Christopher Fauletd7cee712020-04-21 13:45:00 +02005586 node = ebpt_first(&shared_tcpchecks);
5587 while (node) {
5588 next = ebpt_next(node);
5589 ebpt_delete(node);
5590 free(node->key);
5591 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005592 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5593 LIST_DEL(&r->list);
5594 free_tcpcheck(r, 0);
5595 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005596 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005597 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005598 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005599}
Christopher Faulete5870d82020-04-15 11:32:03 +02005600
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005601
Christopher Faulet61cc8522020-04-20 14:54:42 +02005602REGISTER_POST_SERVER_CHECK(init_srv_check);
5603REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5604REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5605REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005606
Christopher Faulet61cc8522020-04-20 14:54:42 +02005607REGISTER_SERVER_DEINIT(deinit_srv_check);
5608REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5609REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5610REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005611
Christopher Faulet61cc8522020-04-20 14:54:42 +02005612/**************************************************************************/
5613/****************************** Email alerts ******************************/
5614/* NOTE: It may be pertinent to use an applet to handle email alerts */
5615/* instead of a tcp-check ruleset */
5616/**************************************************************************/
5617void email_alert_free(struct email_alert *alert)
5618{
5619 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005620
Christopher Faulet61cc8522020-04-20 14:54:42 +02005621 if (!alert)
5622 return;
5623
5624 if (alert->rules.list) {
5625 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5626 LIST_DEL(&rule->list);
5627 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005628 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005629 free_tcpcheck_vars(&alert->rules.preset_vars);
5630 free(alert->rules.list);
5631 alert->rules.list = NULL;
5632 }
5633 pool_free(pool_head_email_alert, alert);
5634}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005635
Christopher Faulet61cc8522020-04-20 14:54:42 +02005636static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5637{
5638 struct check *check = context;
5639 struct email_alertq *q;
5640 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005641
Christopher Faulet61cc8522020-04-20 14:54:42 +02005642 q = container_of(check, typeof(*q), check);
5643
5644 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5645 while (1) {
5646 if (!(check->state & CHK_ST_ENABLED)) {
5647 if (LIST_ISEMPTY(&q->email_alerts)) {
5648 /* All alerts processed, queue the task */
5649 t->expire = TICK_ETERNITY;
5650 task_queue(t);
5651 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005652 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005653
5654 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5655 LIST_DEL(&alert->list);
5656 t->expire = now_ms;
5657 check->tcpcheck_rules = &alert->rules;
5658 check->status = HCHK_STATUS_INI;
5659 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005660 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005661
5662 process_chk(t, context, state);
5663 if (check->state & CHK_ST_INPROGRESS)
5664 break;
5665
5666 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5667 email_alert_free(alert);
5668 check->tcpcheck_rules = NULL;
5669 check->server = NULL;
5670 check->state &= ~CHK_ST_ENABLED;
5671 }
5672 end:
5673 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5674 return t;
5675}
5676
5677/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5678 *
5679 * The function returns 1 in success case, otherwise, it returns 0 and err is
5680 * filled.
5681 */
5682int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5683{
5684 struct mailer *mailer;
5685 struct email_alertq *queues;
5686 const char *err_str;
5687 int i = 0;
5688
5689 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5690 memprintf(err, "out of memory while allocating mailer alerts queues");
5691 goto fail_no_queue;
5692 }
5693
5694 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5695 struct email_alertq *q = &queues[i];
5696 struct check *check = &q->check;
5697 struct task *t;
5698
5699 LIST_INIT(&q->email_alerts);
5700 HA_SPIN_INIT(&q->lock);
5701 check->inter = mls->timeout.mail;
5702 check->rise = DEF_AGENT_RISETIME;
5703 check->proxy = p;
5704 check->fall = DEF_AGENT_FALLTIME;
5705 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5706 memprintf(err, "%s", err_str);
5707 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005708 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005709
5710 check->xprt = mailer->xprt;
5711 check->addr = mailer->addr;
5712 check->port = get_host_port(&mailer->addr);
5713
5714 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5715 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005716 goto error;
5717 }
5718
Christopher Faulet61cc8522020-04-20 14:54:42 +02005719 check->task = t;
5720 t->process = process_email_alert;
5721 t->context = check;
5722
5723 /* check this in one ms */
5724 t->expire = TICK_ETERNITY;
5725 check->start = now;
5726 task_queue(t);
5727 }
5728
5729 mls->users++;
5730 free(p->email_alert.mailers.name);
5731 p->email_alert.mailers.m = mls;
5732 p->email_alert.queues = queues;
5733 return 0;
5734
5735 error:
5736 for (i = 0; i < mls->count; i++) {
5737 struct email_alertq *q = &queues[i];
5738 struct check *check = &q->check;
5739
5740 free_check(check);
5741 }
5742 free(queues);
5743 fail_no_queue:
5744 return 1;
5745}
5746
5747static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5748{
5749 struct tcpcheck_rule *tcpcheck, *prev_check;
5750 struct tcpcheck_expect *expect;
5751
5752 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5753 return 0;
5754 memset(tcpcheck, 0, sizeof(*tcpcheck));
5755 tcpcheck->action = TCPCHK_ACT_EXPECT;
5756
5757 expect = &tcpcheck->expect;
5758 expect->type = TCPCHK_EXPECT_STRING;
5759 LIST_INIT(&expect->onerror_fmt);
5760 LIST_INIT(&expect->onsuccess_fmt);
5761 expect->ok_status = HCHK_STATUS_L7OKD;
5762 expect->err_status = HCHK_STATUS_L7RSP;
5763 expect->tout_status = HCHK_STATUS_L7TOUT;
5764 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005765 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005766 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5767 return 0;
5768 }
5769
5770 /* All tcp-check expect points back to the first inverse expect rule
5771 * in a chain of one or more expect rule, potentially itself.
5772 */
5773 tcpcheck->expect.head = tcpcheck;
5774 list_for_each_entry_rev(prev_check, rules->list, list) {
5775 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5776 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5777 tcpcheck->expect.head = prev_check;
5778 continue;
5779 }
5780 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5781 break;
5782 }
5783 LIST_ADDQ(rules->list, &tcpcheck->list);
5784 return 1;
5785}
5786
5787static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5788{
5789 struct tcpcheck_rule *tcpcheck;
5790 struct tcpcheck_send *send;
5791 const char *in;
5792 char *dst;
5793 int i;
5794
5795 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5796 return 0;
5797 memset(tcpcheck, 0, sizeof(*tcpcheck));
5798 tcpcheck->action = TCPCHK_ACT_SEND;
5799
5800 send = &tcpcheck->send;
5801 send->type = TCPCHK_SEND_STRING;
5802
5803 for (i = 0; strs[i]; i++)
5804 send->data.len += strlen(strs[i]);
5805
Christopher Fauletb61caf42020-04-21 10:57:42 +02005806 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005807 if (!isttest(send->data)) {
5808 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5809 return 0;
5810 }
5811
Christopher Fauletb61caf42020-04-21 10:57:42 +02005812 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005813 for (i = 0; strs[i]; i++)
5814 for (in = strs[i]; (*dst = *in++); dst++);
5815 *dst = 0;
5816
5817 LIST_ADDQ(rules->list, &tcpcheck->list);
5818 return 1;
5819}
5820
5821static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5822 struct email_alertq *q, const char *msg)
5823{
5824 struct email_alert *alert;
5825 struct tcpcheck_rule *tcpcheck;
5826 struct check *check = &q->check;
5827
5828 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5829 goto error;
5830 LIST_INIT(&alert->list);
5831 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5832 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5833 if (!alert->rules.list)
5834 goto error;
5835 LIST_INIT(alert->rules.list);
5836 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5837 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005838
Christopher Faulet61cc8522020-04-20 14:54:42 +02005839 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5840 goto error;
5841 memset(tcpcheck, 0, sizeof(*tcpcheck));
5842 tcpcheck->action = TCPCHK_ACT_CONNECT;
5843 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005844
Christopher Faulet61cc8522020-04-20 14:54:42 +02005845 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005846
Christopher Faulet61cc8522020-04-20 14:54:42 +02005847 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005848 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005849
5850 {
5851 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5852 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5853 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005854 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005855
Christopher Faulet61cc8522020-04-20 14:54:42 +02005856 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5857 goto error;
5858
5859 {
5860 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5861 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005862 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005863 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005864
5865 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5866 goto error;
5867
5868 {
5869 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5870 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005871 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005872 }
5873
Christopher Faulet61cc8522020-04-20 14:54:42 +02005874 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5875 goto error;
5876
5877 {
5878 const char * const strs[2] = { "DATA\r\n" };
5879 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005880 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005881 }
5882
5883 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5884 goto error;
5885
5886 {
5887 struct tm tm;
5888 char datestr[48];
5889 const char * const strs[18] = {
5890 "From: ", p->email_alert.from, "\r\n",
5891 "To: ", p->email_alert.to, "\r\n",
5892 "Date: ", datestr, "\r\n",
5893 "Subject: [HAproxy Alert] ", msg, "\r\n",
5894 "\r\n",
5895 msg, "\r\n",
5896 "\r\n",
5897 ".\r\n",
5898 NULL
5899 };
5900
5901 get_localtime(date.tv_sec, &tm);
5902
5903 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005904 goto error;
5905 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005906
5907 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005908 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005909 }
5910
5911 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005912 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005913
5914 {
5915 const char * const strs[2] = { "QUIT\r\n" };
5916 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5917 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005918 }
5919
Christopher Faulet61cc8522020-04-20 14:54:42 +02005920 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5921 goto error;
5922
5923 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5924 task_wakeup(check->task, TASK_WOKEN_MSG);
5925 LIST_ADDQ(&q->email_alerts, &alert->list);
5926 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5927 return 1;
5928
5929error:
5930 email_alert_free(alert);
5931 return 0;
5932}
5933
5934static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5935{
5936 int i;
5937 struct mailer *mailer;
5938
5939 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5940 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5941 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5942 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5943 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005944 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005945 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005946
Christopher Faulet61cc8522020-04-20 14:54:42 +02005947 return;
5948}
5949
5950/*
5951 * Send email alert if configured.
5952 */
5953void send_email_alert(struct server *s, int level, const char *format, ...)
5954{
5955 va_list argp;
5956 char buf[1024];
5957 int len;
5958 struct proxy *p = s->proxy;
5959
5960 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5961 return;
5962
5963 va_start(argp, format);
5964 len = vsnprintf(buf, sizeof(buf), format, argp);
5965 va_end(argp);
5966
5967 if (len < 0 || len >= sizeof(buf)) {
5968 ha_alert("Email alert [%s] could not format message\n", p->id);
5969 return;
5970 }
5971
5972 enqueue_email_alert(p, s, buf);
5973}
5974
5975/**************************************************************************/
5976/************************** Check sample fetches **************************/
5977/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005978
Christopher Faulet61cc8522020-04-20 14:54:42 +02005979static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005980 { /* END */ },
5981}};
5982
5983INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5984
5985
5986/**************************************************************************/
5987/************************ Check's parsing functions ***********************/
5988/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005989/* Parses the "tcp-check" proxy keyword */
5990static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5991 struct proxy *defpx, const char *file, int line,
5992 char **errmsg)
5993{
Christopher Faulet404f9192020-04-09 23:13:54 +02005994 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005995 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005996 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005997
5998 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5999 ret = 1;
6000
Christopher Faulet404f9192020-04-09 23:13:54 +02006001 /* Deduce the ruleset name from the proxy info */
6002 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6003 ((curpx == defpx) ? "defaults" : curpx->id),
6004 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02006005
Christopher Faulet61cc8522020-04-20 14:54:42 +02006006 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006007 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006008 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006009 if (rs == NULL) {
6010 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006011 goto error;
6012 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006013 }
6014
Gaetan Rivet5301b012020-02-25 17:19:17 +01006015 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02006016 if (!LIST_ISEMPTY(&rs->rules)) {
6017 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01006018 index = chk->index + 1;
6019 }
6020
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006021 cur_arg = 1;
6022 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006023 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006024 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
6025 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006026 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006027 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02006028 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006029 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006030 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006031 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01006032 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
6033
6034 if (!kw) {
6035 action_kw_tcp_check_build_list(&trash);
6036 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
6037 "%s%s. but got '%s'",
6038 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6039 goto error;
6040 }
Christopher Faulet404f9192020-04-09 23:13:54 +02006041 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006042 }
6043
6044 if (!chk) {
6045 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6046 goto error;
6047 }
Christopher Faulet528f4812020-04-28 10:47:28 +02006048 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006049
6050 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01006051 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02006052 LIST_ADDQ(&rs->rules, &chk->list);
6053
6054 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02006055 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006056 /* Use this ruleset if the proxy already has tcp-check enabled */
6057 curpx->tcpcheck_rules.list = &rs->rules;
6058 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
6059 }
6060 else {
6061 /* mark this ruleset as unused for now */
6062 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
6063 }
6064
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006065 return ret;
6066
6067 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006068 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006069 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006070 return -1;
6071}
6072
Christopher Faulet51b129f2020-04-09 15:54:18 +02006073/* Parses the "http-check" proxy keyword */
6074static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6075 struct proxy *defpx, const char *file, int line,
6076 char **errmsg)
6077{
Christopher Faulete5870d82020-04-15 11:32:03 +02006078 struct tcpcheck_ruleset *rs = NULL;
6079 struct tcpcheck_rule *chk = NULL;
6080 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006081
6082 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6083 ret = 1;
6084
6085 cur_arg = 1;
6086 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6087 /* enable a graceful server shutdown on an HTTP 404 response */
6088 curpx->options |= PR_O_DISABLE404;
6089 if (too_many_args(1, args, errmsg, NULL))
6090 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006091 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006092 }
6093 else if (strcmp(args[cur_arg], "send-state") == 0) {
6094 /* enable emission of the apparent state of a server in HTTP checks */
6095 curpx->options2 |= PR_O2_CHK_SNDST;
6096 if (too_many_args(1, args, errmsg, NULL))
6097 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006098 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006099 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006100
Christopher Faulete5870d82020-04-15 11:32:03 +02006101 /* Deduce the ruleset name from the proxy info */
6102 chunk_printf(&trash, "*http-check-%s_%s-%d",
6103 ((curpx == defpx) ? "defaults" : curpx->id),
6104 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006105
Christopher Faulet61cc8522020-04-20 14:54:42 +02006106 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006107 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006108 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006109 if (rs == NULL) {
6110 memprintf(errmsg, "out of memory.\n");
6111 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006112 }
6113 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006114
Christopher Faulete5870d82020-04-15 11:32:03 +02006115 index = 0;
6116 if (!LIST_ISEMPTY(&rs->rules)) {
6117 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6118 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6119 index = chk->index + 1;
6120 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006121
Christopher Faulete5870d82020-04-15 11:32:03 +02006122 if (strcmp(args[cur_arg], "connect") == 0)
6123 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6124 else if (strcmp(args[cur_arg], "send") == 0)
6125 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6126 else if (strcmp(args[cur_arg], "expect") == 0)
6127 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6128 file, line, errmsg);
6129 else if (strcmp(args[cur_arg], "comment") == 0)
6130 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6131 else {
6132 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006133
Christopher Faulete5870d82020-04-15 11:32:03 +02006134 if (!kw) {
6135 action_kw_tcp_check_build_list(&trash);
6136 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6137 " 'send', 'expect'%s%s. but got '%s'",
6138 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6139 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006140 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006141 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6142 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006143
Christopher Faulete5870d82020-04-15 11:32:03 +02006144 if (!chk) {
6145 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6146 goto error;
6147 }
6148 ret = (*errmsg != NULL); /* Handle warning */
6149
6150 chk->index = index;
6151 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6152 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6153 /* Use this ruleset if the proxy already has http-check enabled */
6154 curpx->tcpcheck_rules.list = &rs->rules;
6155 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6156 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6157 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6158 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006159 goto error;
6160 }
6161 }
6162 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006163 /* mark this ruleset as unused for now */
6164 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6165 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006166 }
6167
Christopher Faulete5870d82020-04-15 11:32:03 +02006168 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006169 return ret;
6170
6171 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006172 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006173 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006174 return -1;
6175}
6176
Christopher Faulete9111b62020-04-09 18:12:08 +02006177/* Parses the "external-check" proxy keyword */
6178static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6179 struct proxy *defpx, const char *file, int line,
6180 char **errmsg)
6181{
6182 int cur_arg, ret = 0;
6183
6184 cur_arg = 1;
6185 if (!*(args[cur_arg])) {
6186 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6187 goto error;
6188 }
6189
6190 if (strcmp(args[cur_arg], "command") == 0) {
6191 if (too_many_args(2, args, errmsg, NULL))
6192 goto error;
6193 if (!*(args[cur_arg+1])) {
6194 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6195 goto error;
6196 }
6197 free(curpx->check_command);
6198 curpx->check_command = strdup(args[cur_arg+1]);
6199 }
6200 else if (strcmp(args[cur_arg], "path") == 0) {
6201 if (too_many_args(2, args, errmsg, NULL))
6202 goto error;
6203 if (!*(args[cur_arg+1])) {
6204 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6205 goto error;
6206 }
6207 free(curpx->check_path);
6208 curpx->check_path = strdup(args[cur_arg+1]);
6209 }
6210 else {
6211 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6212 args[0], args[1]);
6213 goto error;
6214 }
6215
6216 ret = (*errmsg != NULL); /* Handle warning */
6217 return ret;
6218
6219error:
6220 return -1;
6221}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006222
Christopher Faulet430e4802020-04-09 15:28:16 +02006223/* Parses the "option tcp-check" proxy keyword */
6224int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6225 const char *file, int line)
6226{
Christopher Faulet404f9192020-04-09 23:13:54 +02006227 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006228 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6229 int err_code = 0;
6230
6231 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6232 err_code |= ERR_WARN;
6233
6234 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6235 goto out;
6236
Christopher Faulet404f9192020-04-09 23:13:54 +02006237 curpx->options2 &= ~PR_O2_CHK_ANY;
6238 curpx->options2 |= PR_O2_TCPCHK_CHK;
6239
Christopher Fauletd7e63962020-04-17 20:15:59 +02006240 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006241 /* If a tcp-check rulesset is already set, do nothing */
6242 if (rules->list)
6243 goto out;
6244
6245 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6246 * get it.
6247 */
6248 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6249 goto curpx_ruleset;
6250
6251 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6252 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006253 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006254 if (rs)
6255 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006256 }
6257
Christopher Faulet404f9192020-04-09 23:13:54 +02006258 curpx_ruleset:
6259 /* Deduce the ruleset name from the proxy info */
6260 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6261 ((curpx == defpx) ? "defaults" : curpx->id),
6262 curpx->conf.file, curpx->conf.line);
6263
Christopher Faulet61cc8522020-04-20 14:54:42 +02006264 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006265 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006266 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006267 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006268 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6269 goto error;
6270 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006271 }
6272
Christopher Faulet404f9192020-04-09 23:13:54 +02006273 ruleset_found:
6274 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006275 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006276 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006277
6278 out:
6279 return err_code;
6280
6281 error:
6282 err_code |= ERR_ALERT | ERR_FATAL;
6283 goto out;
6284}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006285
6286/* Parses the "option redis-check" proxy keyword */
6287int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6288 const char *file, int line)
6289{
6290 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6291 static char *redis_res = "+PONG\r\n";
6292
6293 struct tcpcheck_ruleset *rs = NULL;
6294 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6295 struct tcpcheck_rule *chk;
6296 char *errmsg = NULL;
6297 int err_code = 0;
6298
6299 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6300 err_code |= ERR_WARN;
6301
6302 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6303 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006304
6305 curpx->options2 &= ~PR_O2_CHK_ANY;
6306 curpx->options2 |= PR_O2_TCPCHK_CHK;
6307
6308 free_tcpcheck_vars(&rules->preset_vars);
6309 rules->list = NULL;
6310 rules->flags = 0;
6311
Christopher Faulet61cc8522020-04-20 14:54:42 +02006312 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006313 if (rs)
6314 goto ruleset_found;
6315
Christopher Faulet61cc8522020-04-20 14:54:42 +02006316 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006317 if (rs == NULL) {
6318 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6319 goto error;
6320 }
6321
6322 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6323 1, curpx, &rs->rules, file, line, &errmsg);
6324 if (!chk) {
6325 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6326 goto error;
6327 }
6328 chk->index = 0;
6329 LIST_ADDQ(&rs->rules, &chk->list);
6330
6331 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6332 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006333 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006334 "on-success", "Redis server is ok",
6335 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006336 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006337 if (!chk) {
6338 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6339 goto error;
6340 }
6341 chk->index = 1;
6342 LIST_ADDQ(&rs->rules, &chk->list);
6343
Christopher Fauletd7cee712020-04-21 13:45:00 +02006344 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006345
6346 ruleset_found:
6347 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006348 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006349
6350 out:
6351 free(errmsg);
6352 return err_code;
6353
6354 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006355 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006356 err_code |= ERR_ALERT | ERR_FATAL;
6357 goto out;
6358}
6359
Christopher Faulet811f78c2020-04-01 11:10:27 +02006360
6361/* Parses the "option ssl-hello-chk" proxy keyword */
6362int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6363 const char *file, int line)
6364{
6365 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6366 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6367 *
6368 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6369 */
6370 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006371 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006372 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6373 "0079" /* ContentLength : 0x79 bytes after this one */
6374 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6375 "000075" /* HandshakeLength : 0x75 bytes after this one */
6376 "0300" /* Hello Version : 0x0300 = v3 */
6377 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6378 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6379 "00" /* Session ID length : empty (no session ID) */
6380 "004E" /* Cipher Suite Length : 78 bytes after this one */
6381 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6382 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6383 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6384 "000D" "000E" "000F" "0010" /* various bit lengths, */
6385 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6386 "0015" "0016" "0017" "0018"
6387 "0019" "001A" "001B" "002F"
6388 "0030" "0031" "0032" "0033"
6389 "0034" "0035" "0036" "0037"
6390 "0038" "0039" "003A"
6391 "01" /* Compression Length : 0x01 = 1 byte for types */
6392 "00" /* Compression Type : 0x00 = NULL compression */
6393 };
6394
6395 struct tcpcheck_ruleset *rs = NULL;
6396 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6397 struct tcpcheck_rule *chk;
6398 char *errmsg = NULL;
6399 int err_code = 0;
6400
6401 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6402 err_code |= ERR_WARN;
6403
6404 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6405 goto out;
6406
Christopher Faulet811f78c2020-04-01 11:10:27 +02006407 curpx->options2 &= ~PR_O2_CHK_ANY;
6408 curpx->options2 |= PR_O2_TCPCHK_CHK;
6409
6410 free_tcpcheck_vars(&rules->preset_vars);
6411 rules->list = NULL;
6412 rules->flags = 0;
6413
Christopher Faulet61cc8522020-04-20 14:54:42 +02006414 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006415 if (rs)
6416 goto ruleset_found;
6417
Christopher Faulet61cc8522020-04-20 14:54:42 +02006418 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006419 if (rs == NULL) {
6420 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6421 goto error;
6422 }
6423
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006424 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006425 1, curpx, &rs->rules, file, line, &errmsg);
6426 if (!chk) {
6427 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6428 goto error;
6429 }
6430 chk->index = 0;
6431 LIST_ADDQ(&rs->rules, &chk->list);
6432
6433 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006434 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006435 "error-status", "L6RSP", "tout-status", "L6TOUT",
6436 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006437 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006438 if (!chk) {
6439 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6440 goto error;
6441 }
6442 chk->index = 1;
6443 LIST_ADDQ(&rs->rules, &chk->list);
6444
Christopher Fauletd7cee712020-04-21 13:45:00 +02006445 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006446
6447 ruleset_found:
6448 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006449 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006450
6451 out:
6452 free(errmsg);
6453 return err_code;
6454
6455 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006456 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006457 err_code |= ERR_ALERT | ERR_FATAL;
6458 goto out;
6459}
6460
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006461/* Parses the "option smtpchk" proxy keyword */
6462int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6463 const char *file, int line)
6464{
6465 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6466
6467 struct tcpcheck_ruleset *rs = NULL;
6468 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6469 struct tcpcheck_rule *chk;
6470 struct tcpcheck_var *var = NULL;
6471 char *cmd = NULL, *errmsg = NULL;
6472 int err_code = 0;
6473
6474 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6475 err_code |= ERR_WARN;
6476
6477 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6478 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006479
6480 curpx->options2 &= ~PR_O2_CHK_ANY;
6481 curpx->options2 |= PR_O2_TCPCHK_CHK;
6482
6483 free_tcpcheck_vars(&rules->preset_vars);
6484 rules->list = NULL;
6485 rules->flags = 0;
6486
6487 cur_arg += 2;
6488 if (*args[cur_arg] && *args[cur_arg+1] &&
6489 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6490 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6491 if (cmd)
6492 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6493 }
6494 else {
6495 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6496 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6497 cmd = strdup("HELO localhost");
6498 }
6499
Christopher Fauletb61caf42020-04-21 10:57:42 +02006500 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006501 if (cmd == NULL || var == NULL) {
6502 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6503 goto error;
6504 }
6505 var->data.type = SMP_T_STR;
6506 var->data.u.str.area = cmd;
6507 var->data.u.str.data = strlen(cmd);
6508 LIST_INIT(&var->list);
6509 LIST_ADDQ(&rules->preset_vars, &var->list);
6510 cmd = NULL;
6511 var = NULL;
6512
Christopher Faulet61cc8522020-04-20 14:54:42 +02006513 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006514 if (rs)
6515 goto ruleset_found;
6516
Christopher Faulet61cc8522020-04-20 14:54:42 +02006517 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006518 if (rs == NULL) {
6519 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6520 goto error;
6521 }
6522
6523 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6524 1, curpx, &rs->rules, file, line, &errmsg);
6525 if (!chk) {
6526 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6527 goto error;
6528 }
6529 chk->index = 0;
6530 LIST_ADDQ(&rs->rules, &chk->list);
6531
6532 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6533 "min-recv", "4",
6534 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006535 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006536 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006537 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006538 if (!chk) {
6539 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6540 goto error;
6541 }
6542 chk->index = 1;
6543 LIST_ADDQ(&rs->rules, &chk->list);
6544
6545 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6546 "min-recv", "4",
6547 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006548 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6549 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006550 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006551 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006552 if (!chk) {
6553 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6554 goto error;
6555 }
6556 chk->index = 2;
6557 LIST_ADDQ(&rs->rules, &chk->list);
6558
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006559 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006560 1, curpx, &rs->rules, file, line, &errmsg);
6561 if (!chk) {
6562 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6563 goto error;
6564 }
6565 chk->index = 3;
6566 LIST_ADDQ(&rs->rules, &chk->list);
6567
6568 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6569 "min-recv", "4",
6570 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006571 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6572 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6573 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006574 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006575 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006576 if (!chk) {
6577 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6578 goto error;
6579 }
6580 chk->index = 4;
6581 LIST_ADDQ(&rs->rules, &chk->list);
6582
Christopher Fauletd7cee712020-04-21 13:45:00 +02006583 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006584
6585 ruleset_found:
6586 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006587 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006588
6589 out:
6590 free(errmsg);
6591 return err_code;
6592
6593 error:
6594 free(cmd);
6595 free(var);
6596 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006597 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006598 err_code |= ERR_ALERT | ERR_FATAL;
6599 goto out;
6600}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006601
Christopher Fauletce355072020-04-02 11:44:39 +02006602/* Parses the "option pgsql-check" proxy keyword */
6603int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6604 const char *file, int line)
6605{
6606 static char pgsql_req[] = {
6607 "%[var(check.plen),htonl,hex]" /* The packet length*/
6608 "00030000" /* the version 3.0 */
6609 "7573657200" /* "user" key */
6610 "%[var(check.username),hex]00" /* the username */
6611 "00"
6612 };
6613
6614 struct tcpcheck_ruleset *rs = NULL;
6615 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6616 struct tcpcheck_rule *chk;
6617 struct tcpcheck_var *var = NULL;
6618 char *user = NULL, *errmsg = NULL;
6619 size_t packetlen = 0;
6620 int err_code = 0;
6621
6622 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6623 err_code |= ERR_WARN;
6624
6625 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6626 goto out;
6627
Christopher Fauletce355072020-04-02 11:44:39 +02006628 curpx->options2 &= ~PR_O2_CHK_ANY;
6629 curpx->options2 |= PR_O2_TCPCHK_CHK;
6630
6631 free_tcpcheck_vars(&rules->preset_vars);
6632 rules->list = NULL;
6633 rules->flags = 0;
6634
6635 cur_arg += 2;
6636 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6637 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6638 file, line, args[0], args[1]);
6639 goto error;
6640 }
6641 if (strcmp(args[cur_arg], "user") == 0) {
6642 packetlen = 15 + strlen(args[cur_arg+1]);
6643 user = strdup(args[cur_arg+1]);
6644
Christopher Fauletb61caf42020-04-21 10:57:42 +02006645 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006646 if (user == NULL || var == NULL) {
6647 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6648 goto error;
6649 }
6650 var->data.type = SMP_T_STR;
6651 var->data.u.str.area = user;
6652 var->data.u.str.data = strlen(user);
6653 LIST_INIT(&var->list);
6654 LIST_ADDQ(&rules->preset_vars, &var->list);
6655 user = NULL;
6656 var = NULL;
6657
Christopher Fauletb61caf42020-04-21 10:57:42 +02006658 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006659 if (var == NULL) {
6660 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6661 goto error;
6662 }
6663 var->data.type = SMP_T_SINT;
6664 var->data.u.sint = packetlen;
6665 LIST_INIT(&var->list);
6666 LIST_ADDQ(&rules->preset_vars, &var->list);
6667 var = NULL;
6668 }
6669 else {
6670 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6671 file, line, args[0], args[1]);
6672 goto error;
6673 }
6674
Christopher Faulet61cc8522020-04-20 14:54:42 +02006675 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006676 if (rs)
6677 goto ruleset_found;
6678
Christopher Faulet61cc8522020-04-20 14:54:42 +02006679 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006680 if (rs == NULL) {
6681 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6682 goto error;
6683 }
6684
6685 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6686 1, curpx, &rs->rules, file, line, &errmsg);
6687 if (!chk) {
6688 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6689 goto error;
6690 }
6691 chk->index = 0;
6692 LIST_ADDQ(&rs->rules, &chk->list);
6693
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006694 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006695 1, curpx, &rs->rules, file, line, &errmsg);
6696 if (!chk) {
6697 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6698 goto error;
6699 }
6700 chk->index = 1;
6701 LIST_ADDQ(&rs->rules, &chk->list);
6702
6703 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6704 "min-recv", "5",
6705 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006706 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006707 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006708 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006709 if (!chk) {
6710 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6711 goto error;
6712 }
6713 chk->index = 2;
6714 LIST_ADDQ(&rs->rules, &chk->list);
6715
Christopher Fauletb841c742020-04-27 18:29:49 +02006716 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 +02006717 "min-recv", "9",
6718 "error-status", "L7STS",
6719 "on-success", "PostgreSQL server is ok",
6720 "on-error", "PostgreSQL unknown error",
6721 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006722 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006723 if (!chk) {
6724 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6725 goto error;
6726 }
6727 chk->index = 3;
6728 LIST_ADDQ(&rs->rules, &chk->list);
6729
Christopher Fauletd7cee712020-04-21 13:45:00 +02006730 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletce355072020-04-02 11:44:39 +02006731
6732 ruleset_found:
6733 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006734 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006735
6736 out:
6737 free(errmsg);
6738 return err_code;
6739
6740 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006741 free(user);
6742 free(var);
6743 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006744 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006745 err_code |= ERR_ALERT | ERR_FATAL;
6746 goto out;
6747}
6748
6749
6750/* Parses the "option mysql-check" proxy keyword */
6751int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6752 const char *file, int line)
6753{
6754 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6755 * const char mysql40_client_auth_pkt[] = {
6756 * "\x0e\x00\x00" // packet length
6757 * "\x01" // packet number
6758 * "\x00\x00" // client capabilities
6759 * "\x00\x00\x01" // max packet
6760 * "haproxy\x00" // username (null terminated string)
6761 * "\x00" // filler (always 0x00)
6762 * "\x01\x00\x00" // packet length
6763 * "\x00" // packet number
6764 * "\x01" // COM_QUIT command
6765 * };
6766 */
6767 static char mysql40_rsname[] = "*mysql40-check";
6768 static char mysql40_req[] = {
6769 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6770 "0080" /* client capabilities */
6771 "000001" /* max packet */
6772 "%[var(check.username),hex]00" /* the username */
6773 "00" /* filler (always 0x00) */
6774 "010000" /* packet length*/
6775 "00" /* sequence ID */
6776 "01" /* COM_QUIT command */
6777 };
6778
6779 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6780 * const char mysql41_client_auth_pkt[] = {
6781 * "\x0e\x00\x00\" // packet length
6782 * "\x01" // packet number
6783 * "\x00\x00\x00\x00" // client capabilities
6784 * "\x00\x00\x00\x01" // max packet
6785 * "\x21" // character set (UTF-8)
6786 * char[23] // All zeroes
6787 * "haproxy\x00" // username (null terminated string)
6788 * "\x00" // filler (always 0x00)
6789 * "\x01\x00\x00" // packet length
6790 * "\x00" // packet number
6791 * "\x01" // COM_QUIT command
6792 * };
6793 */
6794 static char mysql41_rsname[] = "*mysql41-check";
6795 static char mysql41_req[] = {
6796 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6797 "00820000" /* client capabilities */
6798 "00800001" /* max packet */
6799 "21" /* character set (UTF-8) */
6800 "000000000000000000000000" /* 23 bytes, al zeroes */
6801 "0000000000000000000000"
6802 "%[var(check.username),hex]00" /* the username */
6803 "00" /* filler (always 0x00) */
6804 "010000" /* packet length*/
6805 "00" /* sequence ID */
6806 "01" /* COM_QUIT command */
6807 };
6808
6809 struct tcpcheck_ruleset *rs = NULL;
6810 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6811 struct tcpcheck_rule *chk;
6812 struct tcpcheck_var *var = NULL;
6813 char *mysql_rsname = "*mysql-check";
6814 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6815 int index = 0, err_code = 0;
6816
6817 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6818 err_code |= ERR_WARN;
6819
6820 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6821 goto out;
6822
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006823 curpx->options2 &= ~PR_O2_CHK_ANY;
6824 curpx->options2 |= PR_O2_TCPCHK_CHK;
6825
6826 free_tcpcheck_vars(&rules->preset_vars);
6827 rules->list = NULL;
6828 rules->flags = 0;
6829
6830 cur_arg += 2;
6831 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006832 int packetlen, userlen;
6833
6834 if (strcmp(args[cur_arg], "user") != 0) {
6835 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6836 file, line, args[0], args[1], args[cur_arg]);
6837 goto error;
6838 }
6839
6840 if (*(args[cur_arg+1]) == 0) {
6841 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6842 file, line, args[0], args[1], args[cur_arg]);
6843 goto error;
6844 }
6845
6846 hdr = calloc(4, sizeof(*hdr));
6847 user = strdup(args[cur_arg+1]);
6848 userlen = strlen(args[cur_arg+1]);
6849
6850 if (hdr == NULL || user == NULL) {
6851 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6852 goto error;
6853 }
6854
6855 if (*args[cur_arg+2]) {
6856 if (strcmp(args[cur_arg+2], "post-41") != 0) {
6857 ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41' (got '%s').\n",
6858 file, line, args[cur_arg], args[cur_arg+2]);
6859 goto error;
6860 }
6861 packetlen = userlen + 7 + 27;
6862 mysql_req = mysql41_req;
6863 mysql_rsname = mysql41_rsname;
6864 }
6865 else {
6866 packetlen = userlen + 7;
6867 mysql_req = mysql40_req;
6868 mysql_rsname = mysql40_rsname;
6869 }
6870
6871 hdr[0] = (unsigned char)(packetlen & 0xff);
6872 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6873 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6874 hdr[3] = 1;
6875
Christopher Fauletb61caf42020-04-21 10:57:42 +02006876 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006877 if (var == NULL) {
6878 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6879 goto error;
6880 }
6881 var->data.type = SMP_T_STR;
6882 var->data.u.str.area = hdr;
6883 var->data.u.str.data = 4;
6884 LIST_INIT(&var->list);
6885 LIST_ADDQ(&rules->preset_vars, &var->list);
6886 hdr = NULL;
6887 var = NULL;
6888
Christopher Fauletb61caf42020-04-21 10:57:42 +02006889 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006890 if (var == NULL) {
6891 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6892 goto error;
6893 }
6894 var->data.type = SMP_T_STR;
6895 var->data.u.str.area = user;
6896 var->data.u.str.data = strlen(user);
6897 LIST_INIT(&var->list);
6898 LIST_ADDQ(&rules->preset_vars, &var->list);
6899 user = NULL;
6900 var = NULL;
6901 }
6902
Christopher Faulet61cc8522020-04-20 14:54:42 +02006903 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006904 if (rs)
6905 goto ruleset_found;
6906
Christopher Faulet61cc8522020-04-20 14:54:42 +02006907 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006908 if (rs == NULL) {
6909 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6910 goto error;
6911 }
6912
6913 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6914 1, curpx, &rs->rules, file, line, &errmsg);
6915 if (!chk) {
6916 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6917 goto error;
6918 }
6919 chk->index = index++;
6920 LIST_ADDQ(&rs->rules, &chk->list);
6921
6922 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006923 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006924 1, curpx, &rs->rules, file, line, &errmsg);
6925 if (!chk) {
6926 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6927 goto error;
6928 }
6929 chk->index = index++;
6930 LIST_ADDQ(&rs->rules, &chk->list);
6931 }
6932
6933 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006934 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006935 if (!chk) {
6936 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6937 goto error;
6938 }
6939 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6940 chk->index = index++;
6941 LIST_ADDQ(&rs->rules, &chk->list);
6942
6943 if (mysql_req) {
6944 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006945 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006946 if (!chk) {
6947 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6948 goto error;
6949 }
6950 chk->expect.custom = tcpcheck_mysql_expect_ok;
6951 chk->index = index++;
6952 LIST_ADDQ(&rs->rules, &chk->list);
6953 }
6954
Christopher Fauletd7cee712020-04-21 13:45:00 +02006955 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006956
6957 ruleset_found:
6958 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006959 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006960
6961 out:
6962 free(errmsg);
6963 return err_code;
6964
6965 error:
6966 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006967 free(user);
6968 free(var);
6969 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006970 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006971 err_code |= ERR_ALERT | ERR_FATAL;
6972 goto out;
6973}
6974
Christopher Faulet1997eca2020-04-03 23:13:50 +02006975int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6976 const char *file, int line)
6977{
6978 static char *ldap_req = "300C020101600702010304008000";
6979
6980 struct tcpcheck_ruleset *rs = NULL;
6981 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6982 struct tcpcheck_rule *chk;
6983 char *errmsg = NULL;
6984 int err_code = 0;
6985
6986 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6987 err_code |= ERR_WARN;
6988
6989 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6990 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006991
6992 curpx->options2 &= ~PR_O2_CHK_ANY;
6993 curpx->options2 |= PR_O2_TCPCHK_CHK;
6994
6995 free_tcpcheck_vars(&rules->preset_vars);
6996 rules->list = NULL;
6997 rules->flags = 0;
6998
Christopher Faulet61cc8522020-04-20 14:54:42 +02006999 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02007000 if (rs)
7001 goto ruleset_found;
7002
Christopher Faulet61cc8522020-04-20 14:54:42 +02007003 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02007004 if (rs == NULL) {
7005 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7006 goto error;
7007 }
7008
7009 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
7010 1, curpx, &rs->rules, file, line, &errmsg);
7011 if (!chk) {
7012 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7013 goto error;
7014 }
7015 chk->index = 0;
7016 LIST_ADDQ(&rs->rules, &chk->list);
7017
7018 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
7019 "min-recv", "14",
7020 "on-error", "Not LDAPv3 protocol",
7021 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007022 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007023 if (!chk) {
7024 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7025 goto error;
7026 }
7027 chk->index = 1;
7028 LIST_ADDQ(&rs->rules, &chk->list);
7029
7030 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007031 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007032 if (!chk) {
7033 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7034 goto error;
7035 }
7036 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
7037 chk->index = 2;
7038 LIST_ADDQ(&rs->rules, &chk->list);
7039
Christopher Fauletd7cee712020-04-21 13:45:00 +02007040 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007041
7042 ruleset_found:
7043 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007044 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02007045
7046 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02007047 free(errmsg);
7048 return err_code;
7049
7050 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007051 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007052 err_code |= ERR_ALERT | ERR_FATAL;
7053 goto out;
7054}
7055
7056int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7057 const char *file, int line)
7058{
7059 struct tcpcheck_ruleset *rs = NULL;
7060 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7061 struct tcpcheck_rule *chk;
7062 char *spop_req = NULL;
7063 char *errmsg = NULL;
7064 int spop_len = 0, err_code = 0;
7065
7066 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7067 err_code |= ERR_WARN;
7068
7069 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7070 goto out;
7071
Christopher Faulet267b01b2020-04-04 10:27:09 +02007072 curpx->options2 &= ~PR_O2_CHK_ANY;
7073 curpx->options2 |= PR_O2_TCPCHK_CHK;
7074
7075 free_tcpcheck_vars(&rules->preset_vars);
7076 rules->list = NULL;
7077 rules->flags = 0;
7078
7079
Christopher Faulet61cc8522020-04-20 14:54:42 +02007080 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007081 if (rs)
7082 goto ruleset_found;
7083
Christopher Faulet61cc8522020-04-20 14:54:42 +02007084 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007085 if (rs == NULL) {
7086 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7087 goto error;
7088 }
7089
7090 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7091 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7092 goto error;
7093 }
7094 chunk_reset(&trash);
7095 dump_binary(&trash, spop_req, spop_len);
7096 trash.area[trash.data] = '\0';
7097
7098 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7099 1, curpx, &rs->rules, file, line, &errmsg);
7100 if (!chk) {
7101 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7102 goto error;
7103 }
7104 chk->index = 0;
7105 LIST_ADDQ(&rs->rules, &chk->list);
7106
7107 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007108 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007109 if (!chk) {
7110 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7111 goto error;
7112 }
7113 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7114 chk->index = 1;
7115 LIST_ADDQ(&rs->rules, &chk->list);
7116
Christopher Fauletd7cee712020-04-21 13:45:00 +02007117 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007118
7119 ruleset_found:
7120 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007121 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007122
7123 out:
7124 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007125 free(errmsg);
7126 return err_code;
7127
7128 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007129 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007130 err_code |= ERR_ALERT | ERR_FATAL;
7131 goto out;
7132}
Christopher Fauletce355072020-04-02 11:44:39 +02007133
Christopher Faulete5870d82020-04-15 11:32:03 +02007134
7135struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7136{
7137 struct tcpcheck_rule *chk = NULL;
7138 struct tcpcheck_http_hdr *hdr = NULL;
7139 char *meth = NULL, *uri = NULL, *vsn = NULL;
7140 char *hdrs, *body;
7141
7142 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7143 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7144 if (hdrs == body)
7145 hdrs = NULL;
7146 if (hdrs) {
7147 *hdrs = '\0';
7148 hdrs +=2;
7149 }
7150 if (body) {
7151 *body = '\0';
7152 body += 4;
7153 }
7154 if (hdrs || body) {
7155 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7156 " Please, consider to use 'http-check send' directive instead.");
7157 }
7158
7159 chk = calloc(1, sizeof(*chk));
7160 if (!chk) {
7161 memprintf(errmsg, "out of memory");
7162 goto error;
7163 }
7164 chk->action = TCPCHK_ACT_SEND;
7165 chk->send.type = TCPCHK_SEND_HTTP;
7166 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7167 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7168 LIST_INIT(&chk->send.http.hdrs);
7169
7170 /* Copy the method, uri and version */
7171 if (*args[cur_arg]) {
7172 if (!*args[cur_arg+1])
7173 uri = args[cur_arg];
7174 else
7175 meth = args[cur_arg];
7176 }
7177 if (*args[cur_arg+1])
7178 uri = args[cur_arg+1];
7179 if (*args[cur_arg+2])
7180 vsn = args[cur_arg+2];
7181
7182 if (meth) {
7183 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7184 chk->send.http.meth.str.area = strdup(meth);
7185 chk->send.http.meth.str.data = strlen(meth);
7186 if (!chk->send.http.meth.str.area) {
7187 memprintf(errmsg, "out of memory");
7188 goto error;
7189 }
7190 }
7191 if (uri) {
7192 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007193 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007194 memprintf(errmsg, "out of memory");
7195 goto error;
7196 }
7197 }
7198 if (vsn) {
7199 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007200 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007201 memprintf(errmsg, "out of memory");
7202 goto error;
7203 }
7204 }
7205
7206 /* Copy the header */
7207 if (hdrs) {
7208 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7209 struct h1m h1m;
7210 int i, ret;
7211
7212 /* Build and parse the request */
7213 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7214
7215 h1m.flags = H1_MF_HDRS_ONLY;
7216 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7217 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7218 &h1m, NULL);
7219 if (ret <= 0) {
7220 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7221 goto error;
7222 }
7223
Christopher Fauletb61caf42020-04-21 10:57:42 +02007224 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007225 hdr = calloc(1, sizeof(*hdr));
7226 if (!hdr) {
7227 memprintf(errmsg, "out of memory");
7228 goto error;
7229 }
7230 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007231 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007232 if (!hdr->name.ptr) {
7233 memprintf(errmsg, "out of memory");
7234 goto error;
7235 }
7236
Christopher Fauletb61caf42020-04-21 10:57:42 +02007237 ist0(tmp_hdrs[i].v);
7238 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 +02007239 goto error;
7240 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7241 }
7242 }
7243
7244 /* Copy the body */
7245 if (body) {
7246 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007247 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007248 memprintf(errmsg, "out of memory");
7249 goto error;
7250 }
7251 }
7252
7253 return chk;
7254
7255 error:
7256 free_tcpcheck_http_hdr(hdr);
7257 free_tcpcheck(chk, 0);
7258 return NULL;
7259}
7260
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007261int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7262 const char *file, int line)
7263{
Christopher Faulete5870d82020-04-15 11:32:03 +02007264 struct tcpcheck_ruleset *rs = NULL;
7265 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7266 struct tcpcheck_rule *chk;
7267 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007268 int err_code = 0;
7269
7270 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7271 err_code |= ERR_WARN;
7272
7273 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7274 goto out;
7275
Christopher Faulete5870d82020-04-15 11:32:03 +02007276 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7277 if (!chk) {
7278 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7279 goto error;
7280 }
7281 if (errmsg) {
7282 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7283 err_code |= ERR_WARN;
7284 free(errmsg);
7285 errmsg = NULL;
7286 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007287
Christopher Faulete5870d82020-04-15 11:32:03 +02007288 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007289 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007290 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007291
Christopher Faulete5870d82020-04-15 11:32:03 +02007292 free_tcpcheck_vars(&rules->preset_vars);
7293 rules->list = NULL;
7294 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007295
Christopher Faulete5870d82020-04-15 11:32:03 +02007296 /* Deduce the ruleset name from the proxy info */
7297 chunk_printf(&trash, "*http-check-%s_%s-%d",
7298 ((curpx == defpx) ? "defaults" : curpx->id),
7299 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007300
Christopher Faulet61cc8522020-04-20 14:54:42 +02007301 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007302 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007303 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007304 if (rs == NULL) {
7305 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7306 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007307 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007308 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007309
Christopher Faulete5870d82020-04-15 11:32:03 +02007310 rules->list = &rs->rules;
7311 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7312 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7313 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7314 rules->list = NULL;
7315 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007316 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007317
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007318 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007319 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007320 return err_code;
7321
7322 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007323 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007324 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007325 err_code |= ERR_ALERT | ERR_FATAL;
7326 goto out;
7327}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007328
Christopher Faulet6f557912020-04-09 15:58:50 +02007329int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7330 const char *file, int line)
7331{
7332 int err_code = 0;
7333
Christopher Faulet6f557912020-04-09 15:58:50 +02007334 curpx->options2 &= ~PR_O2_CHK_ANY;
7335 curpx->options2 |= PR_O2_EXT_CHK;
7336 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7337 goto out;
7338
7339 out:
7340 return err_code;
7341}
7342
Christopher Fauletce8111e2020-04-06 15:04:11 +02007343/* Parse the "addr" server keyword */
7344static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7345 char **errmsg)
7346{
7347 struct sockaddr_storage *sk;
7348 struct protocol *proto;
7349 int port1, port2, err_code = 0;
7350
7351
7352 if (!*args[*cur_arg+1]) {
7353 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7354 goto error;
7355 }
7356
7357 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7358 if (!sk) {
7359 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7360 goto error;
7361 }
7362
7363 proto = protocol_by_family(sk->ss_family);
7364 if (!proto || !proto->connect) {
7365 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7366 args[*cur_arg], args[*cur_arg+1]);
7367 goto error;
7368 }
7369
7370 if (port1 != port2) {
7371 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7372 args[*cur_arg], args[*cur_arg+1]);
7373 goto error;
7374 }
7375
7376 srv->check.addr = srv->agent.addr = *sk;
7377 srv->flags |= SRV_F_CHECKADDR;
7378 srv->flags |= SRV_F_AGENTADDR;
7379
7380 out:
7381 return err_code;
7382
7383 error:
7384 err_code |= ERR_ALERT | ERR_FATAL;
7385 goto out;
7386}
7387
7388
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007389/* Parse the "agent-addr" server keyword */
7390static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7391 char **errmsg)
7392{
7393 int err_code = 0;
7394
7395 if (!*(args[*cur_arg+1])) {
7396 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7397 goto error;
7398 }
7399 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7400 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7401 goto error;
7402 }
7403
7404 out:
7405 return err_code;
7406
7407 error:
7408 err_code |= ERR_ALERT | ERR_FATAL;
7409 goto out;
7410}
7411
7412/* Parse the "agent-check" server keyword */
7413static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7414 char **errmsg)
7415{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007416 struct tcpcheck_ruleset *rs = NULL;
7417 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7418 struct tcpcheck_rule *chk;
7419 int err_code = 0;
7420
7421 if (srv->do_agent)
7422 goto out;
7423
7424 if (!rules) {
7425 rules = calloc(1, sizeof(*rules));
7426 if (!rules) {
7427 memprintf(errmsg, "out of memory.");
7428 goto error;
7429 }
7430 LIST_INIT(&rules->preset_vars);
7431 srv->agent.tcpcheck_rules = rules;
7432 }
7433 rules->list = NULL;
7434 rules->flags = 0;
7435
Christopher Faulet61cc8522020-04-20 14:54:42 +02007436 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007437 if (rs)
7438 goto ruleset_found;
7439
Christopher Faulet61cc8522020-04-20 14:54:42 +02007440 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007441 if (rs == NULL) {
7442 memprintf(errmsg, "out of memory.");
7443 goto error;
7444 }
7445
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007446 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007447 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7448 if (!chk) {
7449 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7450 goto error;
7451 }
7452 chk->index = 0;
7453 LIST_ADDQ(&rs->rules, &chk->list);
7454
7455 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007456 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7457 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007458 if (!chk) {
7459 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7460 goto error;
7461 }
7462 chk->expect.custom = tcpcheck_agent_expect_reply;
7463 chk->index = 1;
7464 LIST_ADDQ(&rs->rules, &chk->list);
7465
Christopher Fauletd7cee712020-04-21 13:45:00 +02007466 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007467
7468 ruleset_found:
7469 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007470 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007471 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007472
7473 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007474 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007475
7476 error:
7477 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007478 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007479 err_code |= ERR_ALERT | ERR_FATAL;
7480 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007481}
7482
7483/* Parse the "agent-inter" server keyword */
7484static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7485 char **errmsg)
7486{
7487 const char *err = NULL;
7488 unsigned int delay;
7489 int err_code = 0;
7490
7491 if (!*(args[*cur_arg+1])) {
7492 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7493 goto error;
7494 }
7495
7496 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7497 if (err == PARSE_TIME_OVER) {
7498 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7499 args[*cur_arg+1], args[*cur_arg], srv->id);
7500 goto error;
7501 }
7502 else if (err == PARSE_TIME_UNDER) {
7503 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7504 args[*cur_arg+1], args[*cur_arg], srv->id);
7505 goto error;
7506 }
7507 else if (err) {
7508 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7509 *err, srv->id);
7510 goto error;
7511 }
7512 if (delay <= 0) {
7513 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7514 delay, args[*cur_arg], srv->id);
7515 goto error;
7516 }
7517 srv->agent.inter = delay;
7518
7519 out:
7520 return err_code;
7521
7522 error:
7523 err_code |= ERR_ALERT | ERR_FATAL;
7524 goto out;
7525}
7526
7527/* Parse the "agent-port" server keyword */
7528static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7529 char **errmsg)
7530{
7531 int err_code = 0;
7532
7533 if (!*(args[*cur_arg+1])) {
7534 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7535 goto error;
7536 }
7537
7538 global.maxsock++;
7539 srv->agent.port = atol(args[*cur_arg+1]);
7540
7541 out:
7542 return err_code;
7543
7544 error:
7545 err_code |= ERR_ALERT | ERR_FATAL;
7546 goto out;
7547}
7548
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007549int set_srv_agent_send(struct server *srv, const char *send)
7550{
7551 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7552 struct tcpcheck_var *var = NULL;
7553 char *str;
7554
7555 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007556 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007557 if (str == NULL || var == NULL)
7558 goto error;
7559
7560 free_tcpcheck_vars(&rules->preset_vars);
7561
7562 var->data.type = SMP_T_STR;
7563 var->data.u.str.area = str;
7564 var->data.u.str.data = strlen(str);
7565 LIST_INIT(&var->list);
7566 LIST_ADDQ(&rules->preset_vars, &var->list);
7567
7568 return 1;
7569
7570 error:
7571 free(str);
7572 free(var);
7573 return 0;
7574}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007575
7576/* Parse the "agent-send" server keyword */
7577static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7578 char **errmsg)
7579{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007580 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007581 int err_code = 0;
7582
7583 if (!*(args[*cur_arg+1])) {
7584 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7585 goto error;
7586 }
7587
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007588 if (!rules) {
7589 rules = calloc(1, sizeof(*rules));
7590 if (!rules) {
7591 memprintf(errmsg, "out of memory.");
7592 goto error;
7593 }
7594 LIST_INIT(&rules->preset_vars);
7595 srv->agent.tcpcheck_rules = rules;
7596 }
7597
7598 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007599 memprintf(errmsg, "out of memory.");
7600 goto error;
7601 }
7602
7603 out:
7604 return err_code;
7605
7606 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007607 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007608 err_code |= ERR_ALERT | ERR_FATAL;
7609 goto out;
7610}
7611
7612/* Parse the "no-agent-send" server keyword */
7613static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7614 char **errmsg)
7615{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007616 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007617 return 0;
7618}
7619
Christopher Fauletce8111e2020-04-06 15:04:11 +02007620/* Parse the "check" server keyword */
7621static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7622 char **errmsg)
7623{
7624 srv->do_check = 1;
7625 return 0;
7626}
7627
7628/* Parse the "check-send-proxy" server keyword */
7629static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7630 char **errmsg)
7631{
7632 srv->check.send_proxy = 1;
7633 return 0;
7634}
7635
7636/* Parse the "check-via-socks4" server keyword */
7637static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7638 char **errmsg)
7639{
7640 srv->check.via_socks4 = 1;
7641 return 0;
7642}
7643
7644/* Parse the "no-check" server keyword */
7645static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7646 char **errmsg)
7647{
7648 deinit_srv_check(srv);
7649 return 0;
7650}
7651
7652/* Parse the "no-check-send-proxy" server keyword */
7653static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7654 char **errmsg)
7655{
7656 srv->check.send_proxy = 0;
7657 return 0;
7658}
7659
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007660/* parse the "check-proto" server keyword */
7661static int srv_parse_check_proto(char **args, int *cur_arg,
7662 struct proxy *px, struct server *newsrv, char **err)
7663{
7664 int err_code = 0;
7665
7666 if (!*args[*cur_arg + 1]) {
7667 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7668 goto error;
7669 }
7670 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7671 if (!newsrv->check.mux_proto) {
7672 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7673 goto error;
7674 }
7675
7676 out:
7677 return err_code;
7678
7679 error:
7680 err_code |= ERR_ALERT | ERR_FATAL;
7681 goto out;
7682}
7683
7684
Christopher Fauletce8111e2020-04-06 15:04:11 +02007685/* Parse the "rise" server keyword */
7686static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7687 char **errmsg)
7688{
7689 int err_code = 0;
7690
7691 if (!*args[*cur_arg + 1]) {
7692 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7693 goto error;
7694 }
7695
7696 srv->check.rise = atol(args[*cur_arg+1]);
7697 if (srv->check.rise <= 0) {
7698 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7699 goto error;
7700 }
7701
7702 if (srv->check.health)
7703 srv->check.health = srv->check.rise;
7704
7705 out:
7706 return err_code;
7707
7708 error:
7709 deinit_srv_agent_check(srv);
7710 err_code |= ERR_ALERT | ERR_FATAL;
7711 goto out;
7712 return 0;
7713}
7714
7715/* Parse the "fall" server keyword */
7716static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7717 char **errmsg)
7718{
7719 int err_code = 0;
7720
7721 if (!*args[*cur_arg + 1]) {
7722 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7723 goto error;
7724 }
7725
7726 srv->check.fall = atol(args[*cur_arg+1]);
7727 if (srv->check.fall <= 0) {
7728 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7729 goto error;
7730 }
7731
7732 out:
7733 return err_code;
7734
7735 error:
7736 deinit_srv_agent_check(srv);
7737 err_code |= ERR_ALERT | ERR_FATAL;
7738 goto out;
7739 return 0;
7740}
7741
7742/* Parse the "inter" server keyword */
7743static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7744 char **errmsg)
7745{
7746 const char *err = NULL;
7747 unsigned int delay;
7748 int err_code = 0;
7749
7750 if (!*(args[*cur_arg+1])) {
7751 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7752 goto error;
7753 }
7754
7755 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7756 if (err == PARSE_TIME_OVER) {
7757 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7758 args[*cur_arg+1], args[*cur_arg], srv->id);
7759 goto error;
7760 }
7761 else if (err == PARSE_TIME_UNDER) {
7762 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7763 args[*cur_arg+1], args[*cur_arg], srv->id);
7764 goto error;
7765 }
7766 else if (err) {
7767 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7768 *err, srv->id);
7769 goto error;
7770 }
7771 if (delay <= 0) {
7772 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7773 delay, args[*cur_arg], srv->id);
7774 goto error;
7775 }
7776 srv->check.inter = delay;
7777
7778 out:
7779 return err_code;
7780
7781 error:
7782 err_code |= ERR_ALERT | ERR_FATAL;
7783 goto out;
7784}
7785
7786
7787/* Parse the "fastinter" server keyword */
7788static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7789 char **errmsg)
7790{
7791 const char *err = NULL;
7792 unsigned int delay;
7793 int err_code = 0;
7794
7795 if (!*(args[*cur_arg+1])) {
7796 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7797 goto error;
7798 }
7799
7800 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7801 if (err == PARSE_TIME_OVER) {
7802 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7803 args[*cur_arg+1], args[*cur_arg], srv->id);
7804 goto error;
7805 }
7806 else if (err == PARSE_TIME_UNDER) {
7807 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7808 args[*cur_arg+1], args[*cur_arg], srv->id);
7809 goto error;
7810 }
7811 else if (err) {
7812 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7813 *err, srv->id);
7814 goto error;
7815 }
7816 if (delay <= 0) {
7817 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7818 delay, args[*cur_arg], srv->id);
7819 goto error;
7820 }
7821 srv->check.fastinter = delay;
7822
7823 out:
7824 return err_code;
7825
7826 error:
7827 err_code |= ERR_ALERT | ERR_FATAL;
7828 goto out;
7829}
7830
7831
7832/* Parse the "downinter" server keyword */
7833static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7834 char **errmsg)
7835{
7836 const char *err = NULL;
7837 unsigned int delay;
7838 int err_code = 0;
7839
7840 if (!*(args[*cur_arg+1])) {
7841 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7842 goto error;
7843 }
7844
7845 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7846 if (err == PARSE_TIME_OVER) {
7847 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7848 args[*cur_arg+1], args[*cur_arg], srv->id);
7849 goto error;
7850 }
7851 else if (err == PARSE_TIME_UNDER) {
7852 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7853 args[*cur_arg+1], args[*cur_arg], srv->id);
7854 goto error;
7855 }
7856 else if (err) {
7857 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7858 *err, srv->id);
7859 goto error;
7860 }
7861 if (delay <= 0) {
7862 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7863 delay, args[*cur_arg], srv->id);
7864 goto error;
7865 }
7866 srv->check.downinter = delay;
7867
7868 out:
7869 return err_code;
7870
7871 error:
7872 err_code |= ERR_ALERT | ERR_FATAL;
7873 goto out;
7874}
7875
7876/* Parse the "port" server keyword */
7877static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7878 char **errmsg)
7879{
7880 int err_code = 0;
7881
7882 if (!*(args[*cur_arg+1])) {
7883 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7884 goto error;
7885 }
7886
7887 global.maxsock++;
7888 srv->check.port = atol(args[*cur_arg+1]);
7889 srv->flags |= SRV_F_CHECKPORT;
7890
7891 out:
7892 return err_code;
7893
7894 error:
7895 err_code |= ERR_ALERT | ERR_FATAL;
7896 goto out;
7897}
7898
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007899static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007900 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7901 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7902 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007903 { 0, NULL, NULL },
7904}};
7905
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007906static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007907 { "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 +02007908 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7909 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7910 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7911 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7912 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007913 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007914 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007915 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7916 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007917 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007918 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7919 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7920 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7921 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7922 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7923 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7924 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7925 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007926 { NULL, NULL, 0 },
7927}};
7928
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007929INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007930INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007931
Willy Tarreaubd741542010-03-16 18:46:54 +01007932/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007933 * Local variables:
7934 * c-indent-level: 8
7935 * c-basic-offset: 8
7936 * End:
7937 */