blob: d42edf478b00151e7d0d7f58df0e5df87716b13b [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 Faulet56192cc2020-05-29 08:10:50 +0200947/* Creates a new shared tcp-check ruleset and insert it in shared_tcpchecks
948 * tree.
949 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900951{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200952 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900953
Christopher Faulet61cc8522020-04-20 14:54:42 +0200954 rs = calloc(1, sizeof(*rs));
955 if (rs == NULL)
956 return NULL;
957
Christopher Fauletd7cee712020-04-21 13:45:00 +0200958 rs->node.key = strdup(name);
959 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200960 free(rs);
961 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900962 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200963
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200965 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200966 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900967}
968
Christopher Faulet61cc8522020-04-20 14:54:42 +0200969/* Releases memory allocated by a tcp-check ruleset. */
970static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900971{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200972 struct tcpcheck_rule *r, *rb;
973 if (!rs)
974 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200975
Christopher Fauletd7cee712020-04-21 13:45:00 +0200976 ebpt_delete(&rs->node);
977 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200978 list_for_each_entry_safe(r, rb, &rs->rules, list) {
979 LIST_DEL(&r->list);
980 free_tcpcheck(r, 0);
981 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200982 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900983}
984
Christopher Faulet61cc8522020-04-20 14:54:42 +0200985
986/**************************************************************************/
987/**************** Everything about tcp-checks execution *******************/
988/**************************************************************************/
989/* Returns the id of a step in a tcp-check ruleset */
990static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200991{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200992 if (!rule)
993 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900994
Christopher Faulet61cc8522020-04-20 14:54:42 +0200995 /* no last started step => first step */
996 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900997 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900998
Christopher Faulet61cc8522020-04-20 14:54:42 +0200999 /* last step is the first implicit connect */
1000 if (rule->index == 0 &&
1001 rule->action == TCPCHK_ACT_CONNECT &&
1002 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1003 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001004
Christopher Faulet61cc8522020-04-20 14:54:42 +02001005 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001006}
1007
Christopher Faulet61cc8522020-04-20 14:54:42 +02001008/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1009 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001010 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001011static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001012{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001014
Christopher Faulet61cc8522020-04-20 14:54:42 +02001015 list_for_each_entry(r, rules->list, list) {
1016 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1017 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001018 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001019 return NULL;
1020}
Cyril Bontéac92a062014-12-27 22:28:38 +01001021
Christopher Faulet61cc8522020-04-20 14:54:42 +02001022/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1023 * NULL if none was found.
1024 */
1025static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1026{
1027 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001028
Christopher Faulet61cc8522020-04-20 14:54:42 +02001029 list_for_each_entry_rev(r, rules->list, list) {
1030 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1031 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001032 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001033 return NULL;
1034}
Cyril Bontéac92a062014-12-27 22:28:38 +01001035
Christopher Faulet61cc8522020-04-20 14:54:42 +02001036/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1037 * <start> or NULL if non was found. If <start> is NULL, it relies on
1038 * get_first_tcpcheck_rule().
1039 */
1040static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1041{
1042 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001043
Christopher Faulet61cc8522020-04-20 14:54:42 +02001044 if (!start)
1045 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001046
Christopher Faulet61cc8522020-04-20 14:54:42 +02001047 r = LIST_NEXT(&start->list, typeof(r), list);
1048 list_for_each_entry_from(r, rules->list, list) {
1049 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1050 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001051 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001052 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001053}
Simon Horman98637e52014-06-20 12:30:16 +09001054
Simon Horman98637e52014-06-20 12:30:16 +09001055
Christopher Faulet61cc8522020-04-20 14:54:42 +02001056/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1057static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1058 int match, struct ist info)
1059{
1060 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001061
Christopher Faulet61cc8522020-04-20 14:54:42 +02001062 /* Follows these step to produce the info message:
1063 * 1. if info field is already provided, copy it
1064 * 2. if the expect rule provides an onerror log-format string,
1065 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001066 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001067 * 4. Otherwise produce the generic tcp-check info message
1068 */
1069 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001070 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001071 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001072 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001073 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1074 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1075 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001076 }
Simon Horman98637e52014-06-20 12:30:16 +09001077
Christopher Faulet61cc8522020-04-20 14:54:42 +02001078 if (check->type == PR_O2_TCPCHK_CHK &&
1079 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1080 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001081
Christopher Faulet61cc8522020-04-20 14:54:42 +02001082 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1083 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001084 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001085 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1086 break;
1087 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001088 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001089 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001090 tcpcheck_get_step_id(check, rule));
1091 break;
1092 case TCPCHK_EXPECT_BINARY:
1093 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1094 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001095 case TCPCHK_EXPECT_STRING_REGEX:
1096 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1097 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001098 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1099 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001100 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001101 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001102 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001103 case TCPCHK_EXPECT_STRING_LF:
1104 case TCPCHK_EXPECT_HTTP_BODY_LF:
1105 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1106 break;
1107 case TCPCHK_EXPECT_BINARY_LF:
1108 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1109 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001110 case TCPCHK_EXPECT_CUSTOM:
1111 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1112 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001113 case TCPCHK_EXPECT_HTTP_HEADER:
1114 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001115 case TCPCHK_EXPECT_UNDEF:
1116 /* Should never happen. */
1117 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001118 }
1119
Christopher Faulet61cc8522020-04-20 14:54:42 +02001120 comment:
1121 /* If the failing expect rule provides a comment, it is concatenated to
1122 * the info message.
1123 */
1124 if (rule->comment) {
1125 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001126 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001127 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001128
Christopher Faulet61cc8522020-04-20 14:54:42 +02001129 /* Finally, the check status code is set if the failing expect rule
1130 * defines a status expression.
1131 */
1132 if (rule->expect.status_expr) {
1133 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001134 rule->expect.status_expr, SMP_T_STR);
1135
1136 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1137 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001138 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001139 }
Simon Horman98637e52014-06-20 12:30:16 +09001140
Christopher Faulet61cc8522020-04-20 14:54:42 +02001141 *(b_tail(msg)) = '\0';
1142}
Cyril Bontéac92a062014-12-27 22:28:38 +01001143
Christopher Faulet61cc8522020-04-20 14:54:42 +02001144/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1145static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1146 struct ist info)
1147{
1148 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001149
Christopher Faulet61cc8522020-04-20 14:54:42 +02001150 /* Follows these step to produce the info message:
1151 * 1. if info field is already provided, copy it
1152 * 2. if the expect rule provides an onsucces log-format string,
1153 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001154 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001155 * 4. Otherwise produce the generic tcp-check info message
1156 */
1157 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001158 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001159 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1160 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1161 &rule->expect.onsuccess_fmt);
1162 else if (check->type == PR_O2_TCPCHK_CHK &&
1163 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1164 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001165
Christopher Faulet61cc8522020-04-20 14:54:42 +02001166 /* Finally, the check status code is set if the expect rule defines a
1167 * status expression.
1168 */
1169 if (rule->expect.status_expr) {
1170 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001171 rule->expect.status_expr, SMP_T_STR);
1172
1173 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1174 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001175 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001176 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001177
1178 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001179}
1180
Christopher Faulet61cc8522020-04-20 14:54:42 +02001181/* Builds the server state header used by HTTP health-checks */
1182static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001183{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001184 int sv_state;
1185 int ratio;
1186 char addr[46];
1187 char port[6];
1188 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1189 "UP %d/%d", "UP",
1190 "NOLB %d/%d", "NOLB",
1191 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001192
Christopher Faulet61cc8522020-04-20 14:54:42 +02001193 if (!(s->check.state & CHK_ST_ENABLED))
1194 sv_state = 6;
1195 else if (s->cur_state != SRV_ST_STOPPED) {
1196 if (s->check.health == s->check.rise + s->check.fall - 1)
1197 sv_state = 3; /* UP */
1198 else
1199 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001200
Christopher Faulet61cc8522020-04-20 14:54:42 +02001201 if (s->cur_state == SRV_ST_STOPPING)
1202 sv_state += 2;
1203 } else {
1204 if (s->check.health)
1205 sv_state = 1; /* going up */
1206 else
1207 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001208 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001209
Christopher Faulet61cc8522020-04-20 14:54:42 +02001210 chunk_appendf(buf, srv_hlt_st[sv_state],
1211 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1212 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001213
Christopher Faulet61cc8522020-04-20 14:54:42 +02001214 addr_to_str(&s->addr, addr, sizeof(addr));
1215 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1216 snprintf(port, sizeof(port), "%u", s->svc_port);
1217 else
1218 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001219
Christopher Faulet61cc8522020-04-20 14:54:42 +02001220 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1221 addr, port, s->proxy->id, s->id,
1222 global.node,
1223 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1224 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1225 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1226 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001227
Christopher Faulet61cc8522020-04-20 14:54:42 +02001228 if ((s->cur_state == SRV_ST_STARTING) &&
1229 now.tv_sec < s->last_change + s->slowstart &&
1230 now.tv_sec >= s->last_change) {
1231 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1232 chunk_appendf(buf, "; throttle=%d%%", ratio);
1233 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001234
Christopher Faulet61cc8522020-04-20 14:54:42 +02001235 return b_data(buf);
1236}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001237
Christopher Faulet61cc8522020-04-20 14:54:42 +02001238/* Internal functions to parse and validate a MySQL packet in the context of an
1239 * expect rule. It start to parse the input buffer at the offset <offset>. If
1240 * <last_read> is set, no more data are expected.
1241 */
1242static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1243 unsigned int offset, int last_read)
1244{
1245 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1246 enum healthcheck_status status;
1247 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001248 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001249 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001250
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001251
Christopher Faulet61cc8522020-04-20 14:54:42 +02001252 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001253 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001254 if (!last_read)
1255 goto wait_more_data;
1256
1257 /* invalid length or truncated response */
1258 status = HCHK_STATUS_L7RSP;
1259 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001260 }
1261
Christopher Faulet61cc8522020-04-20 14:54:42 +02001262 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1263 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1264 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001265
Christopher Faulet61cc8522020-04-20 14:54:42 +02001266 if (b_data(&check->bi) < offset+plen+4) {
1267 if (!last_read)
1268 goto wait_more_data;
1269
1270 /* invalid length or truncated response */
1271 status = HCHK_STATUS_L7RSP;
1272 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001273 }
Simon Horman98637e52014-06-20 12:30:16 +09001274
Christopher Faulet61cc8522020-04-20 14:54:42 +02001275 if (*b_peek(&check->bi, offset+4) == '\xff') {
1276 /* MySQL Error packet always begin with field_count = 0xff */
1277 status = HCHK_STATUS_L7STS;
1278 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1279 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1280 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1281 goto error;
1282 }
Simon Horman98637e52014-06-20 12:30:16 +09001283
Christopher Faulet61cc8522020-04-20 14:54:42 +02001284 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1285 /* Not the last rule, continue */
1286 goto out;
1287 }
Simon Horman98637e52014-06-20 12:30:16 +09001288
Christopher Faulet61cc8522020-04-20 14:54:42 +02001289 /* We set the MySQL Version in description for information purpose
1290 * FIXME : it can be cool to use MySQL Version for other purpose,
1291 * like mark as down old MySQL server.
1292 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001293 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1294 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001295
Christopher Faulet61cc8522020-04-20 14:54:42 +02001296 out:
1297 free_trash_chunk(msg);
1298 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001299
Christopher Faulet61cc8522020-04-20 14:54:42 +02001300 error:
1301 ret = TCPCHK_EVAL_STOP;
1302 check->code = err;
1303 msg = alloc_trash_chunk();
1304 if (msg)
1305 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1306 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1307 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001308
Christopher Faulet61cc8522020-04-20 14:54:42 +02001309 wait_more_data:
1310 ret = TCPCHK_EVAL_WAIT;
1311 goto out;
1312}
Simon Horman98637e52014-06-20 12:30:16 +09001313
Christopher Faulet61cc8522020-04-20 14:54:42 +02001314/* Custom tcp-check expect function to parse and validate the MySQL initial
1315 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1316 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1317 * error occurred.
1318 */
1319static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1320{
1321 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1322}
Simon Horman98637e52014-06-20 12:30:16 +09001323
Christopher Faulet61cc8522020-04-20 14:54:42 +02001324/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1325 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1326 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1327 * an error occurred.
1328 */
1329static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1330{
1331 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001332
Christopher Faulet61cc8522020-04-20 14:54:42 +02001333 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1334 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1335 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001336
Christopher Faulet61cc8522020-04-20 14:54:42 +02001337 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1338}
Simon Horman98637e52014-06-20 12:30:16 +09001339
Christopher Faulet61cc8522020-04-20 14:54:42 +02001340/* Custom tcp-check expect function to parse and validate the LDAP bind response
1341 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1342 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1343 * error occurred.
1344 */
1345static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1346{
1347 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1348 enum healthcheck_status status;
1349 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001350 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001351 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001352
Christopher Faulet61cc8522020-04-20 14:54:42 +02001353 /* Check if the server speaks LDAP (ASN.1/BER)
1354 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1355 * http://tools.ietf.org/html/rfc4511
1356 */
1357 /* size of LDAPMessage */
1358 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001359
Christopher Faulet61cc8522020-04-20 14:54:42 +02001360 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1361 * messageID: 0x02 0x01 0x01: INTEGER 1
1362 * protocolOp: 0x61: bindResponse
1363 */
1364 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1365 status = HCHK_STATUS_L7RSP;
1366 desc = ist("Not LDAPv3 protocol");
1367 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001368 }
Simon Horman98637e52014-06-20 12:30:16 +09001369
Christopher Faulet61cc8522020-04-20 14:54:42 +02001370 /* size of bindResponse */
1371 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001372
Christopher Faulet61cc8522020-04-20 14:54:42 +02001373 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1374 * ldapResult: 0x0a 0x01: ENUMERATION
1375 */
1376 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1377 status = HCHK_STATUS_L7RSP;
1378 desc = ist("Not LDAPv3 protocol");
1379 goto error;
1380 }
Simon Horman98637e52014-06-20 12:30:16 +09001381
Christopher Faulet61cc8522020-04-20 14:54:42 +02001382 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1383 * resultCode
1384 */
1385 check->code = *(b_head(&check->bi) + msglen + 9);
1386 if (check->code) {
1387 status = HCHK_STATUS_L7STS;
1388 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1389 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001390 }
1391
Christopher Faulet1941bab2020-05-05 07:55:50 +02001392 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1393 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001394
Christopher Faulet61cc8522020-04-20 14:54:42 +02001395 out:
1396 free_trash_chunk(msg);
1397 return ret;
1398
1399 error:
1400 ret = TCPCHK_EVAL_STOP;
1401 msg = alloc_trash_chunk();
1402 if (msg)
1403 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1404 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1405 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001406}
1407
Christopher Faulet61cc8522020-04-20 14:54:42 +02001408/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1409 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1410 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001411 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412static 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 +02001413{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001414 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1415 enum healthcheck_status status;
1416 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001417 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001418 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001419
Willy Tarreaubaaee002006-06-26 02:48:02 +02001420
Christopher Faulet61cc8522020-04-20 14:54:42 +02001421 memcpy(&framesz, b_head(&check->bi), 4);
1422 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001423
Christopher Faulet61cc8522020-04-20 14:54:42 +02001424 if (!last_read && b_data(&check->bi) < (4+framesz))
1425 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001426
Christopher Faulet61cc8522020-04-20 14:54:42 +02001427 memset(b_orig(&trash), 0, b_size(&trash));
1428 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1429 status = HCHK_STATUS_L7RSP;
1430 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1431 goto error;
1432 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001433
Christopher Faulet1941bab2020-05-05 07:55:50 +02001434 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1435 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001436
Christopher Faulet61cc8522020-04-20 14:54:42 +02001437 out:
1438 free_trash_chunk(msg);
1439 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001440
Christopher Faulet61cc8522020-04-20 14:54:42 +02001441 error:
1442 ret = TCPCHK_EVAL_STOP;
1443 msg = alloc_trash_chunk();
1444 if (msg)
1445 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1446 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1447 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001448
Christopher Faulet61cc8522020-04-20 14:54:42 +02001449 wait_more_data:
1450 ret = TCPCHK_EVAL_WAIT;
1451 goto out;
1452}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001453
Christopher Faulet61cc8522020-04-20 14:54:42 +02001454/* Custom tcp-check expect function to parse and validate the agent-check
1455 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1456 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1457 */
1458static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1459{
1460 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1461 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1462 const char *hs = NULL; /* health status */
1463 const char *as = NULL; /* admin status */
1464 const char *ps = NULL; /* performance status */
1465 const char *cs = NULL; /* maxconn */
1466 const char *err = NULL; /* first error to report */
1467 const char *wrn = NULL; /* first warning to report */
1468 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001469
Christopher Faulet61cc8522020-04-20 14:54:42 +02001470 /* We're getting an agent check response. The agent could
1471 * have been disabled in the mean time with a long check
1472 * still pending. It is important that we ignore the whole
1473 * response.
1474 */
1475 if (!(check->state & CHK_ST_ENABLED))
1476 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001477
Christopher Faulet61cc8522020-04-20 14:54:42 +02001478 /* The agent supports strings made of a single line ended by the
1479 * first CR ('\r') or LF ('\n'). This line is composed of words
1480 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1481 * line may optionally contained a description of a state change
1482 * after a sharp ('#'), which is only considered if a health state
1483 * is announced.
1484 *
1485 * Words may be composed of :
1486 * - a numeric weight suffixed by the percent character ('%').
1487 * - a health status among "up", "down", "stopped", and "fail".
1488 * - an admin status among "ready", "drain", "maint".
1489 *
1490 * These words may appear in any order. If multiple words of the
1491 * same category appear, the last one wins.
1492 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001493
Christopher Faulet61cc8522020-04-20 14:54:42 +02001494 p = b_head(&check->bi);
1495 while (*p && *p != '\n' && *p != '\r')
1496 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001497
Christopher Faulet61cc8522020-04-20 14:54:42 +02001498 if (!*p) {
1499 if (!last_read)
1500 goto wait_more_data;
1501
1502 /* at least inform the admin that the agent is mis-behaving */
1503 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1504 goto out;
1505 }
1506
1507 *p = 0;
1508 cmd = b_head(&check->bi);
1509
1510 while (*cmd) {
1511 /* look for next word */
1512 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1513 cmd++;
1514 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001515 }
1516
Christopher Faulet61cc8522020-04-20 14:54:42 +02001517 if (*cmd == '#') {
1518 /* this is the beginning of a health status description,
1519 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001520 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001521 cmd++;
1522 while (*cmd == '\t' || *cmd == ' ')
1523 cmd++;
1524 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001525 }
1526
Christopher Faulet61cc8522020-04-20 14:54:42 +02001527 /* find the end of the word so that we have a null-terminated
1528 * word between <cmd> and <p>.
1529 */
1530 p = cmd + 1;
1531 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1532 p++;
1533 if (*p)
1534 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001535
Christopher Faulet61cc8522020-04-20 14:54:42 +02001536 /* first, health statuses */
1537 if (strcasecmp(cmd, "up") == 0) {
1538 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1539 status = HCHK_STATUS_L7OKD;
1540 hs = cmd;
1541 }
1542 else if (strcasecmp(cmd, "down") == 0) {
1543 check->server->check.health = 0;
1544 status = HCHK_STATUS_L7STS;
1545 hs = cmd;
1546 }
1547 else if (strcasecmp(cmd, "stopped") == 0) {
1548 check->server->check.health = 0;
1549 status = HCHK_STATUS_L7STS;
1550 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001551 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001552 else if (strcasecmp(cmd, "fail") == 0) {
1553 check->server->check.health = 0;
1554 status = HCHK_STATUS_L7STS;
1555 hs = cmd;
1556 }
1557 /* admin statuses */
1558 else if (strcasecmp(cmd, "ready") == 0) {
1559 as = cmd;
1560 }
1561 else if (strcasecmp(cmd, "drain") == 0) {
1562 as = cmd;
1563 }
1564 else if (strcasecmp(cmd, "maint") == 0) {
1565 as = cmd;
1566 }
1567 /* try to parse a weight here and keep the last one */
1568 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1569 ps = cmd;
1570 }
1571 /* try to parse a maxconn here */
1572 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1573 cs = cmd;
1574 }
1575 else {
1576 /* keep a copy of the first error */
1577 if (!err)
1578 err = cmd;
1579 }
1580 /* skip to next word */
1581 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001582 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001583 /* here, cmd points either to \0 or to the beginning of a
1584 * description. Skip possible leading spaces.
1585 */
1586 while (*cmd == ' ' || *cmd == '\n')
1587 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001588
Christopher Faulet61cc8522020-04-20 14:54:42 +02001589 /* First, update the admin status so that we avoid sending other
1590 * possibly useless warnings and can also update the health if
1591 * present after going back up.
1592 */
1593 if (as) {
1594 if (strcasecmp(as, "drain") == 0)
1595 srv_adm_set_drain(check->server);
1596 else if (strcasecmp(as, "maint") == 0)
1597 srv_adm_set_maint(check->server);
1598 else
1599 srv_adm_set_ready(check->server);
1600 }
Simon Horman98637e52014-06-20 12:30:16 +09001601
Christopher Faulet61cc8522020-04-20 14:54:42 +02001602 /* now change weights */
1603 if (ps) {
1604 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001605
Christopher Faulet61cc8522020-04-20 14:54:42 +02001606 msg = server_parse_weight_change_request(check->server, ps);
1607 if (!wrn || !*wrn)
1608 wrn = msg;
1609 }
Simon Horman98637e52014-06-20 12:30:16 +09001610
Christopher Faulet61cc8522020-04-20 14:54:42 +02001611 if (cs) {
1612 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001613
Christopher Faulet61cc8522020-04-20 14:54:42 +02001614 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001615
Christopher Faulet61cc8522020-04-20 14:54:42 +02001616 msg = server_parse_maxconn_change_request(check->server, cs);
1617 if (!wrn || !*wrn)
1618 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001619 }
1620
Christopher Faulet61cc8522020-04-20 14:54:42 +02001621 /* and finally health status */
1622 if (hs) {
1623 /* We'll report some of the warnings and errors we have
1624 * here. Down reports are critical, we leave them untouched.
1625 * Lack of report, or report of 'UP' leaves the room for
1626 * ERR first, then WARN.
1627 */
1628 const char *msg = cmd;
1629 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001630
Christopher Faulet61cc8522020-04-20 14:54:42 +02001631 if (!*msg || status == HCHK_STATUS_L7OKD) {
1632 if (err && *err)
1633 msg = err;
1634 else if (wrn && *wrn)
1635 msg = wrn;
1636 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001637
Christopher Faulet61cc8522020-04-20 14:54:42 +02001638 t = get_trash_chunk();
1639 chunk_printf(t, "via agent : %s%s%s%s",
1640 hs, *msg ? " (" : "",
1641 msg, *msg ? ")" : "");
1642 set_server_check_status(check, status, t->area);
1643 }
1644 else if (err && *err) {
1645 /* No status change but we'd like to report something odd.
1646 * Just report the current state and copy the message.
1647 */
1648 chunk_printf(&trash, "agent reports an error : %s", err);
1649 set_server_check_status(check, status/*check->status*/, trash.area);
1650 }
1651 else if (wrn && *wrn) {
1652 /* No status change but we'd like to report something odd.
1653 * Just report the current state and copy the message.
1654 */
1655 chunk_printf(&trash, "agent warns : %s", wrn);
1656 set_server_check_status(check, status/*check->status*/, trash.area);
1657 }
1658 else
1659 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001660
Christopher Faulet61cc8522020-04-20 14:54:42 +02001661 out:
1662 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001663
Christopher Faulet61cc8522020-04-20 14:54:42 +02001664 wait_more_data:
1665 ret = TCPCHK_EVAL_WAIT;
1666 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001667}
1668
Christopher Faulet61cc8522020-04-20 14:54:42 +02001669/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1670 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1671 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001672 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001673static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001674{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001675 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1676 struct tcpcheck_connect *connect = &rule->connect;
1677 struct proxy *proxy = check->proxy;
1678 struct server *s = check->server;
1679 struct task *t = check->task;
1680 struct conn_stream *cs;
1681 struct connection *conn = NULL;
1682 struct protocol *proto;
1683 struct xprt_ops *xprt;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001684 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001685 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001686
Christopher Faulet61cc8522020-04-20 14:54:42 +02001687 /* For a connect action we'll create a new connection. We may also have
1688 * to kill a previous one. But we don't want to leave *without* a
1689 * connection if we came here from the connection layer, hence with a
1690 * connection. Thus we'll proceed in the following order :
1691 * 1: close but not release previous connection (handled by the caller)
1692 * 2: try to get a new connection
1693 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001694 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001695
Christopher Faulet61cc8522020-04-20 14:54:42 +02001696 /* 2- prepare new connection */
1697 cs = cs_new(NULL);
1698 if (!cs) {
1699 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1700 tcpcheck_get_step_id(check, rule));
1701 if (rule->comment)
1702 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1703 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1704 ret = TCPCHK_EVAL_STOP;
1705 goto out;
1706 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001707
Christopher Faulet61cc8522020-04-20 14:54:42 +02001708 /* 3- release and replace the old one on success */
1709 if (check->cs) {
1710 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001711 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1712 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001713
1714 /* We may have been scheduled to run, and the I/O handler
1715 * expects to have a cs, so remove the tasklet
1716 */
1717 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1718 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001719 }
1720
Christopher Faulet61cc8522020-04-20 14:54:42 +02001721 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001722
Christopher Faulet61cc8522020-04-20 14:54:42 +02001723 check->cs = cs;
1724 conn = cs->conn;
1725 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001726
Christopher Faulet61cc8522020-04-20 14:54:42 +02001727 /* Maybe there were an older connection we were waiting on */
1728 check->wait_list.events = 0;
1729 conn->target = s ? &s->obj_type : &proxy->obj_type;
1730
1731 /* no client address */
1732 if (!sockaddr_alloc(&conn->dst)) {
1733 status = SF_ERR_RESOURCE;
1734 goto fail_check;
1735 }
1736
1737 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001738 * addr if specified on the server. otherwise, use the server addr (it
1739 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001740 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001741 *conn->dst = (is_addr(&connect->addr)
1742 ? connect->addr
1743 : (is_addr(&check->addr) ? check->addr : s->addr));
1744 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001745
Christopher Faulet61cc8522020-04-20 14:54:42 +02001746 port = 0;
1747 if (!port && connect->port)
1748 port = connect->port;
1749 if (!port && connect->port_expr) {
1750 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001751
Christopher Faulet61cc8522020-04-20 14:54:42 +02001752 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1753 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1754 connect->port_expr, SMP_T_SINT);
1755 if (smp)
1756 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001757 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001758 if (!port && is_inet_addr(&connect->addr))
1759 port = get_host_port(&connect->addr);
1760 if (!port && check->port)
1761 port = check->port;
1762 if (!port && is_inet_addr(&check->addr))
1763 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001764 if (!port) {
1765 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001766 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001767 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001768 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001769
Christopher Faulet61cc8522020-04-20 14:54:42 +02001770 xprt = ((connect->options & TCPCHK_OPT_SSL)
1771 ? xprt_get(XPRT_SSL)
1772 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001773
Christopher Faulet61cc8522020-04-20 14:54:42 +02001774 conn_prepare(conn, proto, xprt);
1775 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001776
Christopher Faulet61cc8522020-04-20 14:54:42 +02001777 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001778 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001779 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001780 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001781
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1783 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001784 if (!next || next->action != TCPCHK_ACT_EXPECT)
1785 flags |= CONNECT_DELACK_ALWAYS;
1786 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001787 }
1788
Christopher Faulet61cc8522020-04-20 14:54:42 +02001789 if (status != SF_ERR_NONE)
1790 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001791
Christopher Faulet61cc8522020-04-20 14:54:42 +02001792 conn->flags |= CO_FL_PRIVATE;
1793 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001794
Christopher Faulet61cc8522020-04-20 14:54:42 +02001795 /* The mux may be initialized now if there isn't server attached to the
1796 * check (email alerts) or if there is a mux proto specified or if there
1797 * is no alpn.
1798 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001799 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1800 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001801 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001802
Christopher Faulet61cc8522020-04-20 14:54:42 +02001803 if (connect->mux_proto)
1804 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001805 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001806 mux_ops = check->mux_proto->mux;
1807 else {
1808 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1809 ? PROTO_MODE_HTTP
1810 : PROTO_MODE_TCP);
1811
1812 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001813 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001814 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1815 status = SF_ERR_INTERNAL;
1816 goto fail_check;
1817 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001818 }
1819
Christopher Faulet61cc8522020-04-20 14:54:42 +02001820#ifdef USE_OPENSSL
1821 if (connect->sni)
1822 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001823 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001824 ssl_sock_set_servername(conn, s->check.sni);
1825
1826 if (connect->alpn)
1827 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001828 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001829 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1830#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001831 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001832 conn->send_proxy_ofs = 1;
1833 conn->flags |= CO_FL_SOCKS4;
1834 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001835 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 +02001836 conn->send_proxy_ofs = 1;
1837 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001838 }
1839
Christopher Faulet61cc8522020-04-20 14:54:42 +02001840 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1841 conn->send_proxy_ofs = 1;
1842 conn->flags |= CO_FL_SEND_PROXY;
1843 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001844 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 +02001845 conn->send_proxy_ofs = 1;
1846 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001847 }
1848
Christopher Faulet61cc8522020-04-20 14:54:42 +02001849 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1850 /* Some servers don't like reset on close */
1851 fdtab[cs->conn->handle.fd].linger_risk = 0;
1852 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001853
Christopher Faulet61cc8522020-04-20 14:54:42 +02001854 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1855 if (xprt_add_hs(conn) < 0)
1856 status = SF_ERR_RESOURCE;
1857 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001858
Christopher Faulet61cc8522020-04-20 14:54:42 +02001859 fail_check:
1860 /* It can return one of :
1861 * - SF_ERR_NONE if everything's OK
1862 * - SF_ERR_SRVTO if there are no more servers
1863 * - SF_ERR_SRVCL if the connection was refused by the server
1864 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1865 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1866 * - SF_ERR_INTERNAL for any other purely internal errors
1867 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1868 * Note that we try to prevent the network stack from sending the ACK during the
1869 * connect() when a pure TCP check is used (without PROXY protocol).
1870 */
1871 switch (status) {
1872 case SF_ERR_NONE:
1873 /* we allow up to min(inter, timeout.connect) for a connection
1874 * to establish but only when timeout.check is set as it may be
1875 * to short for a full check otherwise
1876 */
1877 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001878
Christopher Faulet61cc8522020-04-20 14:54:42 +02001879 if (proxy->timeout.check && proxy->timeout.connect) {
1880 int t_con = tick_add(now_ms, proxy->timeout.connect);
1881 t->expire = tick_first(t->expire, t_con);
1882 }
1883 break;
1884 case SF_ERR_SRVTO: /* ETIMEDOUT */
1885 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1886 case SF_ERR_PRXCOND:
1887 case SF_ERR_RESOURCE:
1888 case SF_ERR_INTERNAL:
1889 chk_report_conn_err(check, errno, 0);
1890 ret = TCPCHK_EVAL_STOP;
1891 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001892 }
1893
Christopher Faulet61cc8522020-04-20 14:54:42 +02001894 /* don't do anything until the connection is established */
1895 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001896 if (conn->mux) {
1897 if (next && next->action == TCPCHK_ACT_SEND)
1898 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1899 else
1900 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1901 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001902 ret = TCPCHK_EVAL_WAIT;
1903 goto out;
1904 }
1905
1906 out:
1907 if (conn && check->result == CHK_RES_FAILED)
1908 conn->flags |= CO_FL_ERROR;
1909 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001910}
1911
Christopher Faulet61cc8522020-04-20 14:54:42 +02001912/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1913 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1914 * TCPCHK_EVAL_STOP if an error occurred.
1915 */
1916static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001917{
1918 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001919 struct tcpcheck_send *send = &rule->send;
1920 struct conn_stream *cs = check->cs;
1921 struct connection *conn = cs_conn(cs);
1922 struct buffer *tmp = NULL;
1923 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001924
Christopher Faulet61cc8522020-04-20 14:54:42 +02001925 /* reset the read & write buffer */
1926 b_reset(&check->bi);
1927 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001928
Christopher Faulet61cc8522020-04-20 14:54:42 +02001929 switch (send->type) {
1930 case TCPCHK_SEND_STRING:
1931 case TCPCHK_SEND_BINARY:
1932 if (istlen(send->data) >= b_size(&check->bo)) {
1933 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1934 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1935 tcpcheck_get_step_id(check, rule));
1936 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1937 ret = TCPCHK_EVAL_STOP;
1938 goto out;
1939 }
1940 b_putist(&check->bo, send->data);
1941 break;
1942 case TCPCHK_SEND_STRING_LF:
1943 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1944 if (!b_data(&check->bo))
1945 goto out;
1946 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001947 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001948 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001949
Christopher Faulet61cc8522020-04-20 14:54:42 +02001950 tmp = alloc_trash_chunk();
1951 if (!tmp)
1952 goto error_lf;
1953 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1954 if (!b_data(tmp))
1955 goto out;
1956 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001957 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001958 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001959 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001960 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001961 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001962 case TCPCHK_SEND_HTTP: {
1963 struct htx_sl *sl;
1964 struct ist meth, uri, vsn, clen, body;
1965 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001966
Christopher Faulet61cc8522020-04-20 14:54:42 +02001967 tmp = alloc_trash_chunk();
1968 if (!tmp)
1969 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001970
Christopher Faulet61cc8522020-04-20 14:54:42 +02001971 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1972 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1973 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001974 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1975 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1976 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1977 }
1978 else
1979 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001980 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001981
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001982 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1983 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001984 slflags |= HTX_SL_F_VER_11;
1985 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1986 if (!isttest(send->http.body))
1987 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001988
Christopher Faulet61cc8522020-04-20 14:54:42 +02001989 htx = htx_from_buf(&check->bo);
1990 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1991 if (!sl)
1992 goto error_htx;
1993 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001994 if (!http_update_host(htx, sl, uri))
1995 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001996
Christopher Faulet61cc8522020-04-20 14:54:42 +02001997 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1998 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001999 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002000
Christopher Faulet61cc8522020-04-20 14:54:42 +02002001 list_for_each_entry(hdr, &send->http.hdrs, list) {
2002 chunk_reset(tmp);
2003 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2004 if (!b_data(tmp))
2005 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002006 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2007 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002008 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002009 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2010 if (!http_update_authority(htx, sl, hdr_value))
2011 goto error_htx;
2012 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002013 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002014
Christopher Faulet61cc8522020-04-20 14:54:42 +02002015 }
2016 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2017 chunk_reset(tmp);
2018 httpchk_build_status_header(check->server, tmp);
2019 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2020 goto error_htx;
2021 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002022
2023
2024 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2025 chunk_reset(tmp);
2026 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2027 body = ist2(b_orig(tmp), b_data(tmp));
2028 }
2029 else
2030 body = send->http.body;
2031 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2032
2033 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2034 !htx_add_header(htx, ist("Content-length"), clen))
2035 goto error_htx;
2036
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002037
Christopher Faulet61cc8522020-04-20 14:54:42 +02002038 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002039 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002040 !htx_add_endof(htx, HTX_BLK_EOM))
2041 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002042
Christopher Faulet61cc8522020-04-20 14:54:42 +02002043 htx_to_buf(htx, &check->bo);
2044 break;
2045 }
2046 case TCPCHK_SEND_UNDEF:
2047 /* Should never happen. */
2048 ret = TCPCHK_EVAL_STOP;
2049 goto out;
2050 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002051
Christopher Faulet6d471212020-04-22 11:09:25 +02002052
2053 if (conn->mux->snd_buf(cs, &check->bo,
2054 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002055 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002056 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002057 goto out;
2058 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002059 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002060 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002061 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2062 ret = TCPCHK_EVAL_WAIT;
2063 goto out;
2064 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002065
Christopher Faulet61cc8522020-04-20 14:54:42 +02002066 out:
2067 free_trash_chunk(tmp);
2068 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002069
Christopher Faulet61cc8522020-04-20 14:54:42 +02002070 error_htx:
2071 if (htx) {
2072 htx_reset(htx);
2073 htx_to_buf(htx, &check->bo);
2074 }
2075 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2076 tcpcheck_get_step_id(check, rule));
2077 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2078 ret = TCPCHK_EVAL_STOP;
2079 goto out;
2080
2081 error_lf:
2082 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2083 tcpcheck_get_step_id(check, rule));
2084 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2085 ret = TCPCHK_EVAL_STOP;
2086 goto out;
2087
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002088}
2089
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002090/* Try to receive data before evaluating a tcp-check expect rule. Returns
2091 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002092 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2093 * TCPCHK_EVAL_STOP if an error occurred.
2094 */
2095static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002096{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002097 struct conn_stream *cs = check->cs;
2098 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002099 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002100 size_t max, read, cur_read = 0;
2101 int is_empty;
2102 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002103
Christopher Faulet61cc8522020-04-20 14:54:42 +02002104 if (check->wait_list.events & SUB_RETRY_RECV)
2105 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002106
Christopher Faulet61cc8522020-04-20 14:54:42 +02002107 if (cs->flags & CS_FL_EOS)
2108 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002109
Christopher Faulet61cc8522020-04-20 14:54:42 +02002110 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002111
Christopher Faulet61cc8522020-04-20 14:54:42 +02002112 /* prepare to detect if the mux needs more room */
2113 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002114
Christopher Faulet61cc8522020-04-20 14:54:42 +02002115 while ((cs->flags & CS_FL_RCV_MORE) ||
2116 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2117 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2118 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2119 cur_read += read;
2120 if (!read ||
2121 (cs->flags & CS_FL_WANT_ROOM) ||
2122 (--read_poll <= 0) ||
2123 (read < max && read >= global.tune.recv_enough))
2124 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002125 }
2126
Christopher Faulet61cc8522020-04-20 14:54:42 +02002127 end_recv:
2128 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2129 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2130 /* Report network errors only if we got no other data. Otherwise
2131 * we'll let the upper layers decide whether the response is OK
2132 * or not. It is very common that an RST sent by the server is
2133 * reported as an error just after the last data chunk.
2134 */
2135 goto stop;
2136 }
2137 if (!cur_read) {
2138 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2139 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2140 goto wait_more_data;
2141 }
2142 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002143 int status;
2144
Christopher Faulet61cc8522020-04-20 14:54:42 +02002145 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2146 tcpcheck_get_step_id(check, rule));
2147 if (rule->comment)
2148 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002149
2150 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2151 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002152 goto stop;
2153 }
2154 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002155
2156 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002157 return ret;
2158
Christopher Faulet61cc8522020-04-20 14:54:42 +02002159 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002160 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002161 goto out;
2162
2163 wait_more_data:
2164 ret = TCPCHK_EVAL_WAIT;
2165 goto out;
2166}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002167
Christopher Faulet61cc8522020-04-20 14:54:42 +02002168/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2169 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2170 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2171 * error occurred.
2172 */
2173static 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 +02002174{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002175 struct htx *htx = htxbuf(&check->bi);
2176 struct htx_sl *sl;
2177 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002178 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002179 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002180 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002181 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002182 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002183 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002184
Christopher Faulet61cc8522020-04-20 14:54:42 +02002185 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002186
Christopher Faulet61cc8522020-04-20 14:54:42 +02002187 if (htx->flags & HTX_FL_PARSING_ERROR) {
2188 status = HCHK_STATUS_L7RSP;
2189 goto error;
2190 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002191
Christopher Faulet61cc8522020-04-20 14:54:42 +02002192 if (htx_is_empty(htx)) {
2193 if (last_read) {
2194 status = HCHK_STATUS_L7RSP;
2195 goto error;
2196 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002197 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002198 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002199
Christopher Faulet61cc8522020-04-20 14:54:42 +02002200 sl = http_get_stline(htx);
2201 check->code = sl->info.res.status;
2202
2203 if (check->server &&
2204 (check->server->proxy->options & PR_O_DISABLE404) &&
2205 (check->server->next_state != SRV_ST_STOPPED) &&
2206 (check->code == 404)) {
2207 /* 404 may be accepted as "stopping" only if the server was up */
2208 goto out;
2209 }
2210
2211 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2212 /* Make GCC happy ; initialize match to a failure state. */
2213 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002214 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002215
2216 switch (expect->type) {
2217 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002218 match = 0;
2219 for (i = 0; i < expect->codes.num; i++) {
2220 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2221 sl->info.res.status <= expect->codes.codes[i][1]) {
2222 match = 1;
2223 break;
2224 }
2225 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002226
2227 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002228 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002229 if (LIST_ISEMPTY(&expect->onerror_fmt))
2230 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002231 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002232 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002233 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2234
2235 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002236 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002237 if (LIST_ISEMPTY(&expect->onerror_fmt))
2238 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002239 break;
2240
Christopher Faulet39708192020-05-05 10:47:36 +02002241 case TCPCHK_EXPECT_HTTP_HEADER: {
2242 struct http_hdr_ctx ctx;
2243 struct ist npat, vpat, value;
2244 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2245
2246 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2247 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002248 if (!nbuf) {
2249 status = HCHK_STATUS_L7RSP;
2250 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002251 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002252 }
Christopher Faulet39708192020-05-05 10:47:36 +02002253 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 +02002254 if (!b_data(nbuf)) {
2255 status = HCHK_STATUS_L7RSP;
2256 desc = ist("log-format string evaluated to an empty string");
2257 goto error;
2258 }
Christopher Faulet39708192020-05-05 10:47:36 +02002259 npat = ist2(b_orig(nbuf), b_data(nbuf));
2260 }
2261 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2262 npat = expect->hdr.name;
2263
2264 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2265 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002266 if (!vbuf) {
2267 status = HCHK_STATUS_L7RSP;
2268 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002269 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002270 }
Christopher Faulet39708192020-05-05 10:47:36 +02002271 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 +02002272 if (!b_data(vbuf)) {
2273 status = HCHK_STATUS_L7RSP;
2274 desc = ist("log-format string evaluated to an empty string");
2275 goto error;
2276 }
Christopher Faulet39708192020-05-05 10:47:36 +02002277 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2278 }
2279 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2280 vpat = expect->hdr.value;
2281
2282 match = 0;
2283 ctx.blk = NULL;
2284 while (1) {
2285 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2286 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2287 if (!http_find_str_header(htx, npat, &ctx, full))
2288 goto end_of_match;
2289 break;
2290 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2291 if (!http_find_pfx_header(htx, npat, &ctx, full))
2292 goto end_of_match;
2293 break;
2294 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2295 if (!http_find_sfx_header(htx, npat, &ctx, full))
2296 goto end_of_match;
2297 break;
2298 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2299 if (!http_find_sub_header(htx, npat, &ctx, full))
2300 goto end_of_match;
2301 break;
2302 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2303 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2304 goto end_of_match;
2305 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002306 default:
2307 /* should never happen */
2308 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002309 }
2310
Christopher Faulet083eff32020-05-07 15:41:39 +02002311 /* A header has matched the name pattern, let's test its
2312 * value now (always defined from there). If there is no
2313 * value pattern, it is a good match.
2314 */
2315
Christopher Faulet39708192020-05-05 10:47:36 +02002316 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2317 match = 1;
2318 goto end_of_match;
2319 }
2320
2321 value = ctx.value;
2322 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2323 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2324 if (isteq(value, vpat)) {
2325 match = 1;
2326 goto end_of_match;
2327 }
2328 break;
2329 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2330 if (istlen(value) < istlen(vpat))
2331 break;
2332 value = ist2(istptr(value), istlen(vpat));
2333 if (isteq(value, vpat)) {
2334 match = 1;
2335 goto end_of_match;
2336 }
2337 break;
2338 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2339 if (istlen(value) < istlen(vpat))
2340 break;
2341 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2342 if (isteq(value, vpat)) {
2343 match = 1;
2344 goto end_of_match;
2345 }
2346 break;
2347 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2348 if (isttest(istist(value, vpat))) {
2349 match = 1;
2350 goto end_of_match;
2351 }
2352 break;
2353 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2354 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2355 match = 1;
2356 goto end_of_match;
2357 }
2358 break;
2359 }
2360 }
2361
2362 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002363 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002364 if (LIST_ISEMPTY(&expect->onerror_fmt))
2365 desc = htx_sl_res_reason(sl);
2366 break;
2367 }
2368
Christopher Faulet61cc8522020-04-20 14:54:42 +02002369 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002370 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002371 case TCPCHK_EXPECT_HTTP_BODY_LF:
2372 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002373 chunk_reset(&trash);
2374 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2375 enum htx_blk_type type = htx_get_blk_type(blk);
2376
2377 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2378 break;
2379 if (type == HTX_BLK_DATA) {
2380 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2381 break;
2382 }
2383 }
2384
2385 if (!b_data(&trash)) {
2386 if (!last_read)
2387 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002388 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002389 if (LIST_ISEMPTY(&expect->onerror_fmt))
2390 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002391 goto error;
2392 }
2393
Christopher Fauletaaab0832020-05-05 15:54:22 +02002394 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2395 tmp = alloc_trash_chunk();
2396 if (!tmp) {
2397 status = HCHK_STATUS_L7RSP;
2398 desc = ist("Failed to allocate buffer to eval log-format string");
2399 goto error;
2400 }
2401 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2402 if (!b_data(tmp)) {
2403 status = HCHK_STATUS_L7RSP;
2404 desc = ist("log-format string evaluated to an empty string");
2405 goto error;
2406 }
2407 }
2408
Christopher Faulet61cc8522020-04-20 14:54:42 +02002409 if (!last_read &&
2410 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002411 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002412 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2413 ret = TCPCHK_EVAL_WAIT;
2414 goto out;
2415 }
2416
2417 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002418 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002419 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2420 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002421 else
2422 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2423
2424 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002425 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002426 if (LIST_ISEMPTY(&expect->onerror_fmt))
2427 desc = (inverse
2428 ? ist("HTTP check matched unwanted content")
2429 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002430 break;
2431
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002432
Christopher Faulet61cc8522020-04-20 14:54:42 +02002433 default:
2434 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002435 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002436 goto error;
2437 }
2438
Christopher Faulet61cc8522020-04-20 14:54:42 +02002439 /* Wait for more data on mismatch only if no minimum is defined (-1),
2440 * otherwise the absence of match is already conclusive.
2441 */
2442 if (!match && !last_read && (expect->min_recv == -1)) {
2443 ret = TCPCHK_EVAL_WAIT;
2444 goto out;
2445 }
2446
2447 if (!(match ^ inverse))
2448 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002449
2450 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002451 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002452 free_trash_chunk(nbuf);
2453 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002454 free_trash_chunk(msg);
2455 return ret;
2456
2457 error:
2458 ret = TCPCHK_EVAL_STOP;
2459 msg = alloc_trash_chunk();
2460 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002461 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002462 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2463 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002464
2465 wait_more_data:
2466 ret = TCPCHK_EVAL_WAIT;
2467 goto out;
2468}
2469
Christopher Faulet61cc8522020-04-20 14:54:42 +02002470/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2471 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2472 * if an error occurred.
2473 */
2474static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2475{
2476 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2477 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002478 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002479 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002480 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002482
Christopher Faulet61cc8522020-04-20 14:54:42 +02002483 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002484
Christopher Faulet61cc8522020-04-20 14:54:42 +02002485 /* The current expect might need more data than the previous one, check again
2486 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002487 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002488 if (!last_read) {
2489 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2490 (b_data(&check->bi) < istlen(expect->data))) {
2491 ret = TCPCHK_EVAL_WAIT;
2492 goto out;
2493 }
2494 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2495 ret = TCPCHK_EVAL_WAIT;
2496 goto out;
2497 }
2498 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002499
Christopher Faulet61cc8522020-04-20 14:54:42 +02002500 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2501 /* Make GCC happy ; initialize match to a failure state. */
2502 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002503 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002504
Christopher Faulet61cc8522020-04-20 14:54:42 +02002505 switch (expect->type) {
2506 case TCPCHK_EXPECT_STRING:
2507 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002508 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 +02002509 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002510 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002511 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 +02002512 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002513
Christopher Faulet67a23452020-05-05 18:10:01 +02002514 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002515 chunk_reset(&trash);
2516 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002517 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002518 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002519
2520 case TCPCHK_EXPECT_STRING_LF:
2521 case TCPCHK_EXPECT_BINARY_LF:
2522 match = 0;
2523 tmp = alloc_trash_chunk();
2524 if (!tmp) {
2525 status = HCHK_STATUS_L7RSP;
2526 desc = ist("Failed to allocate buffer to eval format string");
2527 goto error;
2528 }
2529 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2530 if (!b_data(tmp)) {
2531 status = HCHK_STATUS_L7RSP;
2532 desc = ist("log-format string evaluated to an empty string");
2533 goto error;
2534 }
2535 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2536 int len = tmp->data;
2537 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2538 status = HCHK_STATUS_L7RSP;
2539 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2540 goto error;
2541 }
2542 tmp->data = len;
2543 }
2544 if (b_data(&check->bi) < tmp->data) {
2545 if (!last_read) {
2546 ret = TCPCHK_EVAL_WAIT;
2547 goto out;
2548 }
2549 break;
2550 }
2551 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2552 break;
2553
Christopher Faulet61cc8522020-04-20 14:54:42 +02002554 case TCPCHK_EXPECT_CUSTOM:
2555 if (expect->custom)
2556 ret = expect->custom(check, rule, last_read);
2557 goto out;
2558 default:
2559 /* Should never happen. */
2560 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002561 goto out;
2562 }
2563
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002564
Christopher Faulet61cc8522020-04-20 14:54:42 +02002565 /* Wait for more data on mismatch only if no minimum is defined (-1),
2566 * otherwise the absence of match is already conclusive.
2567 */
2568 if (!match && !last_read && (expect->min_recv == -1)) {
2569 ret = TCPCHK_EVAL_WAIT;
2570 goto out;
2571 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002572
Christopher Faulet61cc8522020-04-20 14:54:42 +02002573 /* Result as expected, next rule. */
2574 if (match ^ inverse)
2575 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002576
Christopher Fauletaaab0832020-05-05 15:54:22 +02002577 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002578 /* From this point on, we matched something we did not want, this is an error state. */
2579 ret = TCPCHK_EVAL_STOP;
2580 msg = alloc_trash_chunk();
2581 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002582 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002583 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002584 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002585
Christopher Faulet61cc8522020-04-20 14:54:42 +02002586 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002587 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002588 return ret;
2589}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002590
Christopher Faulet61cc8522020-04-20 14:54:42 +02002591/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002592 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002593 * waits.
2594 */
2595static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2596{
2597 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2598 struct act_rule *act_rule;
2599 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002600
Christopher Faulet61cc8522020-04-20 14:54:42 +02002601 act_rule =rule->action_kw.rule;
2602 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2603 if (act_ret != ACT_RET_CONT) {
2604 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2605 tcpcheck_get_step_id(check, rule));
2606 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2607 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002608 }
2609
Christopher Faulet61cc8522020-04-20 14:54:42 +02002610 return ret;
2611}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002612
Christopher Faulet61cc8522020-04-20 14:54:42 +02002613/* Executes a tcp-check ruleset. Note that this is called both from the
2614 * connection's wake() callback and from the check scheduling task. It returns
2615 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2616 * presenting the risk of an fd replacement.
2617 *
2618 * Please do NOT place any return statement in this function and only leave
2619 * via the out_end_tcpcheck label after setting retcode.
2620 */
2621static int tcpcheck_main(struct check *check)
2622{
2623 struct tcpcheck_rule *rule;
2624 struct conn_stream *cs = check->cs;
2625 struct connection *conn = cs_conn(cs);
2626 int must_read = 1, last_read = 0;
2627 int ret, retcode = 0;
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002628 enum tcpcheck_eval_ret eval_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002629
Christopher Faulet61cc8522020-04-20 14:54:42 +02002630 /* here, we know that the check is complete or that it failed */
2631 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002632 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002633
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002634 /* Note: the conn-stream and the connection may only be undefined before
2635 * the first rule evaluation (it is always a connect rule) or when the
2636 * conn-stream allocation failed on the first connect.
2637 */
2638
Christopher Faulet61cc8522020-04-20 14:54:42 +02002639 /* 1- check for connection error, if any */
2640 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2641 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002642
Christopher Faulet61cc8522020-04-20 14:54:42 +02002643 /* 2- check if we are waiting for the connection establishment. It only
2644 * happens during TCPCHK_ACT_CONNECT. */
2645 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002646 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002647 struct tcpcheck_rule *next;
2648
2649 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2650 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002651 if (!(check->wait_list.events & SUB_RETRY_SEND))
2652 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002653 goto out;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002654 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002655 else {
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002656 eval_ret = tcpcheck_eval_recv(check, check->current_step);
2657 if (eval_ret == TCPCHK_EVAL_STOP)
2658 goto out_end_tcpcheck;
2659 else if (eval_ret == TCPCHK_EVAL_WAIT)
2660 goto out;
2661 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2662 must_read = 0;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002663 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002664 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002665 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002666 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002667
2668 /* 3- check for pending outgoing data. It only happens during
2669 * TCPCHK_ACT_SEND. */
2670 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002671 if (b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002672 /* We're already waiting to be able to send, give up */
2673 if (check->wait_list.events & SUB_RETRY_SEND)
2674 goto out;
2675
Christopher Faulet6d471212020-04-22 11:09:25 +02002676 ret = conn->mux->snd_buf(cs, &check->bo,
2677 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002678 if (ret <= 0) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002679 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002680 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002681 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002682 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002683 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002684 goto out;
2685 }
2686 }
2687 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002688 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002689
Christopher Faulet61cc8522020-04-20 14:54:42 +02002690 /* 4- check if a rule must be resume. It happens if check->current_step
2691 * is defined. */
2692 else if (check->current_step)
2693 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002694
Christopher Faulet61cc8522020-04-20 14:54:42 +02002695 /* 5- It is the first evaluation. We must create a session and preset
2696 * tcp-check variables */
2697 else {
2698 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002699
Christopher Faulet61cc8522020-04-20 14:54:42 +02002700 /* First evaluation, create a session */
2701 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2702 if (!check->sess) {
2703 chunk_printf(&trash, "TCPCHK error allocating check session");
2704 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2705 goto out_end_tcpcheck;
2706 }
2707 vars_init(&check->vars, SCOPE_CHECK);
2708 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002709
Christopher Faulet61cc8522020-04-20 14:54:42 +02002710 /* Preset tcp-check variables */
2711 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2712 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002713
Christopher Faulet61cc8522020-04-20 14:54:42 +02002714 memset(&smp, 0, sizeof(smp));
2715 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2716 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002717 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002718 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002719 }
2720
Christopher Faulet61cc8522020-04-20 14:54:42 +02002721 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002722
Christopher Faulet61cc8522020-04-20 14:54:42 +02002723 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002724 check->code = 0;
2725 switch (rule->action) {
2726 case TCPCHK_ACT_CONNECT:
2727 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002728
Christopher Faulet61cc8522020-04-20 14:54:42 +02002729 /* close but not release yet previous connection */
2730 if (check->cs) {
2731 cs_close(check->cs);
2732 retcode = -1; /* do not reuse the fd in the caller! */
2733 }
2734 eval_ret = tcpcheck_eval_connect(check, rule);
Christopher Faulet3cbdd222020-05-26 11:14:50 +02002735
2736 /* Refresh conn-stream and connection */
2737 cs = check->cs;
2738 conn = cs_conn(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002739 must_read = 1; last_read = 0;
2740 break;
2741 case TCPCHK_ACT_SEND:
2742 check->current_step = rule;
2743 eval_ret = tcpcheck_eval_send(check, rule);
2744 must_read = 1;
2745 break;
2746 case TCPCHK_ACT_EXPECT:
2747 check->current_step = rule;
2748 if (must_read) {
2749 if (check->proxy->timeout.check)
2750 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002751
Christopher Faulet61cc8522020-04-20 14:54:42 +02002752 eval_ret = tcpcheck_eval_recv(check, rule);
2753 if (eval_ret == TCPCHK_EVAL_STOP)
2754 goto out_end_tcpcheck;
2755 else if (eval_ret == TCPCHK_EVAL_WAIT)
2756 goto out;
2757 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2758 must_read = 0;
2759 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002760
Christopher Faulet61cc8522020-04-20 14:54:42 +02002761 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2762 ? tcpcheck_eval_expect_http(check, rule, last_read)
2763 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002764
Christopher Faulet61cc8522020-04-20 14:54:42 +02002765 if (eval_ret == TCPCHK_EVAL_WAIT) {
2766 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002767 if (!(check->wait_list.events & SUB_RETRY_RECV))
2768 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002769 }
2770 break;
2771 case TCPCHK_ACT_ACTION_KW:
2772 /* Don't update the current step */
2773 eval_ret = tcpcheck_eval_action_kw(check, rule);
2774 break;
2775 default:
2776 /* Otherwise, just go to the next one and don't update
2777 * the current step
2778 */
2779 eval_ret = TCPCHK_EVAL_CONTINUE;
2780 break;
2781 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002782
Christopher Faulet61cc8522020-04-20 14:54:42 +02002783 switch (eval_ret) {
2784 case TCPCHK_EVAL_CONTINUE:
2785 break;
2786 case TCPCHK_EVAL_WAIT:
2787 goto out;
2788 case TCPCHK_EVAL_STOP:
2789 goto out_end_tcpcheck;
2790 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002791 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002792
Christopher Faulet61cc8522020-04-20 14:54:42 +02002793 /* All rules was evaluated */
2794 if (check->current_step) {
2795 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002796
Christopher Faulet61cc8522020-04-20 14:54:42 +02002797 if (rule->action == TCPCHK_ACT_EXPECT) {
2798 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002799 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002800
Christopher Faulet61cc8522020-04-20 14:54:42 +02002801 if (check->server &&
2802 (check->server->proxy->options & PR_O_DISABLE404) &&
2803 (check->server->next_state != SRV_ST_STOPPED) &&
2804 (check->code == 404)) {
2805 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2806 goto out_end_tcpcheck;
2807 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002808
Christopher Faulet61cc8522020-04-20 14:54:42 +02002809 msg = alloc_trash_chunk();
2810 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002811 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002812 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2813 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002814 free_trash_chunk(msg);
2815 }
2816 else if (rule->action == TCPCHK_ACT_CONNECT) {
2817 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002818 enum healthcheck_status status = HCHK_STATUS_L4OK;
2819#ifdef USE_OPENSSL
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002820 if (ssl_sock_is_ssl(conn))
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002821 status = HCHK_STATUS_L6OK;
2822#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002823 set_server_check_status(check, status, msg);
2824 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002825 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002826 else
2827 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002828
Christopher Faulet61cc8522020-04-20 14:54:42 +02002829 out_end_tcpcheck:
2830 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2831 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002832
Christopher Faulet61cc8522020-04-20 14:54:42 +02002833 out:
2834 return retcode;
2835}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002836
Christopher Faulet14cd3162020-04-16 14:50:06 +02002837
Christopher Faulet61cc8522020-04-20 14:54:42 +02002838/**************************************************************************/
2839/************** Health-checks based on an external process ****************/
2840/**************************************************************************/
2841static struct list pid_list = LIST_HEAD_INIT(pid_list);
2842static struct pool_head *pool_head_pid_list;
2843__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002844
Christopher Faulet61cc8522020-04-20 14:54:42 +02002845struct extcheck_env {
2846 char *name; /* environment variable name */
2847 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2848};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002849
Christopher Faulet61cc8522020-04-20 14:54:42 +02002850/* environment variables memory requirement for different types of data */
2851#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2852 * such environment variables are not updatable. */
2853#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2854#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2855#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002856
Christopher Faulet61cc8522020-04-20 14:54:42 +02002857/* external checks environment variables */
2858enum {
2859 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002860
Christopher Faulet61cc8522020-04-20 14:54:42 +02002861 /* Proxy specific environment variables */
2862 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2863 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2864 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2865 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002866
Christopher Faulet61cc8522020-04-20 14:54:42 +02002867 /* Server specific environment variables */
2868 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2869 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2870 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2871 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2872 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2873 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002874
Christopher Faulet61cc8522020-04-20 14:54:42 +02002875 EXTCHK_SIZE
2876};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002877
Christopher Faulet61cc8522020-04-20 14:54:42 +02002878const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2879 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2880 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2881 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2882 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2883 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2884 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2885 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2886 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2887 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2888 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2889 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2890};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002891
Christopher Faulet61cc8522020-04-20 14:54:42 +02002892void block_sigchld(void)
2893{
2894 sigset_t set;
2895 sigemptyset(&set);
2896 sigaddset(&set, SIGCHLD);
2897 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2898}
Willy Tarreaube373152018-09-06 11:45:30 +02002899
Christopher Faulet61cc8522020-04-20 14:54:42 +02002900void unblock_sigchld(void)
2901{
2902 sigset_t set;
2903 sigemptyset(&set);
2904 sigaddset(&set, SIGCHLD);
2905 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002906}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002907
Christopher Faulet61cc8522020-04-20 14:54:42 +02002908static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002909{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002910 struct pid_list *elem;
2911 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002912
Christopher Faulet61cc8522020-04-20 14:54:42 +02002913 elem = pool_alloc(pool_head_pid_list);
2914 if (!elem)
2915 return NULL;
2916 elem->pid = pid;
2917 elem->t = t;
2918 elem->exited = 0;
2919 check->curpid = elem;
2920 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002921
Christopher Faulet61cc8522020-04-20 14:54:42 +02002922 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2923 LIST_ADD(&pid_list, &elem->list);
2924 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002925
Christopher Faulet61cc8522020-04-20 14:54:42 +02002926 return elem;
2927}
Christopher Faulete5870d82020-04-15 11:32:03 +02002928
Christopher Faulet61cc8522020-04-20 14:54:42 +02002929static void pid_list_del(struct pid_list *elem)
2930{
2931 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002932
Christopher Faulet61cc8522020-04-20 14:54:42 +02002933 if (!elem)
2934 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002935
Christopher Faulet61cc8522020-04-20 14:54:42 +02002936 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2937 LIST_DEL(&elem->list);
2938 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002939
Christopher Faulet61cc8522020-04-20 14:54:42 +02002940 if (!elem->exited)
2941 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002942
Christopher Faulet61cc8522020-04-20 14:54:42 +02002943 check = elem->t->context;
2944 check->curpid = NULL;
2945 pool_free(pool_head_pid_list, elem);
2946}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002947
Christopher Faulet61cc8522020-04-20 14:54:42 +02002948/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2949static void pid_list_expire(pid_t pid, int status)
2950{
2951 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002952
Christopher Faulet61cc8522020-04-20 14:54:42 +02002953 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2954 list_for_each_entry(elem, &pid_list, list) {
2955 if (elem->pid == pid) {
2956 elem->t->expire = now_ms;
2957 elem->status = status;
2958 elem->exited = 1;
2959 task_wakeup(elem->t, TASK_WOKEN_IO);
2960 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002961 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002962 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002963 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2964}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002965
Christopher Faulet61cc8522020-04-20 14:54:42 +02002966static void sigchld_handler(struct sig_handler *sh)
2967{
2968 pid_t pid;
2969 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002970
Christopher Faulet61cc8522020-04-20 14:54:42 +02002971 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2972 pid_list_expire(pid, status);
2973}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002974
Christopher Faulet61cc8522020-04-20 14:54:42 +02002975static int init_pid_list(void)
2976{
2977 if (pool_head_pid_list != NULL)
2978 /* Nothing to do */
2979 return 0;
2980
2981 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2982 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2983 strerror(errno));
2984 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002985 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002986
Christopher Faulet61cc8522020-04-20 14:54:42 +02002987 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2988 if (pool_head_pid_list == NULL) {
2989 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2990 strerror(errno));
2991 return 1;
2992 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002993
Christopher Faulet61cc8522020-04-20 14:54:42 +02002994 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002995}
2996
Christopher Faulet61cc8522020-04-20 14:54:42 +02002997/* helper macro to set an environment variable and jump to a specific label on failure. */
2998#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002999
Christopher Faulet61cc8522020-04-20 14:54:42 +02003000/*
3001 * helper function to allocate enough memory to store an environment variable.
3002 * It will also check that the environment variable is updatable, and silently
3003 * fail if not.
3004 */
3005static int extchk_setenv(struct check *check, int idx, const char *value)
3006{
3007 int len, ret;
3008 char *envname;
3009 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003010
Christopher Faulet61cc8522020-04-20 14:54:42 +02003011 if (idx < 0 || idx >= EXTCHK_SIZE) {
3012 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
3013 return 1;
3014 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003015
Christopher Faulet61cc8522020-04-20 14:54:42 +02003016 envname = extcheck_envs[idx].name;
3017 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003018
Christopher Faulet61cc8522020-04-20 14:54:42 +02003019 /* Check if the environment variable is already set, and silently reject
3020 * the update if this one is not updatable. */
3021 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3022 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003023
Christopher Faulet61cc8522020-04-20 14:54:42 +02003024 /* Instead of sending NOT_USED, sending an empty value is preferable */
3025 if (strcmp(value, "NOT_USED") == 0) {
3026 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003027 }
3028
Christopher Faulet61cc8522020-04-20 14:54:42 +02003029 len = strlen(envname) + 1;
3030 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3031 len += strlen(value);
3032 else
3033 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003034
Christopher Faulet61cc8522020-04-20 14:54:42 +02003035 if (!check->envp[idx])
3036 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003037
Christopher Faulet61cc8522020-04-20 14:54:42 +02003038 if (!check->envp[idx]) {
3039 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3040 return 1;
3041 }
3042 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3043 if (ret < 0) {
3044 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3045 return 1;
3046 }
3047 else if (ret > len) {
3048 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3049 return 1;
3050 }
3051 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003052}
3053
Christopher Faulet61cc8522020-04-20 14:54:42 +02003054static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003055{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003056 struct server *s = check->server;
3057 struct proxy *px = s->proxy;
3058 struct listener *listener = NULL, *l;
3059 int i;
3060 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3061 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003062
Christopher Faulet61cc8522020-04-20 14:54:42 +02003063 list_for_each_entry(l, &px->conf.listeners, by_fe)
3064 /* Use the first INET, INET6 or UNIX listener */
3065 if (l->addr.ss_family == AF_INET ||
3066 l->addr.ss_family == AF_INET6 ||
3067 l->addr.ss_family == AF_UNIX) {
3068 listener = l;
3069 break;
3070 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003071
Christopher Faulet61cc8522020-04-20 14:54:42 +02003072 check->curpid = NULL;
3073 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3074 if (!check->envp) {
3075 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3076 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003077 }
3078
Christopher Faulet61cc8522020-04-20 14:54:42 +02003079 check->argv = calloc(6, sizeof(char *));
3080 if (!check->argv) {
3081 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3082 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003083 }
3084
Christopher Faulet61cc8522020-04-20 14:54:42 +02003085 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003086
Christopher Faulet61cc8522020-04-20 14:54:42 +02003087 if (!listener) {
3088 check->argv[1] = strdup("NOT_USED");
3089 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003090 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003091 else if (listener->addr.ss_family == AF_INET ||
3092 listener->addr.ss_family == AF_INET6) {
3093 addr_to_str(&listener->addr, buf, sizeof(buf));
3094 check->argv[1] = strdup(buf);
3095 port_to_str(&listener->addr, buf, sizeof(buf));
3096 check->argv[2] = strdup(buf);
3097 }
3098 else if (listener->addr.ss_family == AF_UNIX) {
3099 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003100
Christopher Faulet61cc8522020-04-20 14:54:42 +02003101 un = (struct sockaddr_un *)&listener->addr;
3102 check->argv[1] = strdup(un->sun_path);
3103 check->argv[2] = strdup("NOT_USED");
3104 }
3105 else {
3106 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3107 goto err;
3108 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003109
Christopher Faulet61cc8522020-04-20 14:54:42 +02003110 if (!check->argv[1] || !check->argv[2]) {
3111 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3112 goto err;
3113 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003114
Christopher Faulet61cc8522020-04-20 14:54:42 +02003115 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3116 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3117 if (!check->argv[3] || !check->argv[4]) {
3118 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3119 goto err;
3120 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003121
Christopher Faulet61cc8522020-04-20 14:54:42 +02003122 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3123 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3124 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003125
Christopher Faulet61cc8522020-04-20 14:54:42 +02003126 for (i = 0; i < 5; i++) {
3127 if (!check->argv[i]) {
3128 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3129 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003130 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003131 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003132
Christopher Faulet61cc8522020-04-20 14:54:42 +02003133 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3134 /* Add proxy environment variables */
3135 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3136 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3137 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3138 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3139 /* Add server environment variables */
3140 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3141 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3142 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3143 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3144 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3145 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003146
Christopher Faulet61cc8522020-04-20 14:54:42 +02003147 /* Ensure that we don't leave any hole in check->envp */
3148 for (i = 0; i < EXTCHK_SIZE; i++)
3149 if (!check->envp[i])
3150 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003151
Christopher Faulet61cc8522020-04-20 14:54:42 +02003152 return 1;
3153err:
3154 if (check->envp) {
3155 for (i = 0; i < EXTCHK_SIZE; i++)
3156 free(check->envp[i]);
3157 free(check->envp);
3158 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003159 }
3160
Christopher Faulet61cc8522020-04-20 14:54:42 +02003161 if (check->argv) {
3162 for (i = 1; i < 5; i++)
3163 free(check->argv[i]);
3164 free(check->argv);
3165 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003166 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003167 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003168}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003169
Christopher Faulet61cc8522020-04-20 14:54:42 +02003170/*
3171 * establish a server health-check that makes use of a process.
3172 *
3173 * It can return one of :
3174 * - SF_ERR_NONE if everything's OK
3175 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3176 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3177 *
3178 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003179 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003180static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003181{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003182 char buf[256];
3183 struct check *check = t->context;
3184 struct server *s = check->server;
3185 struct proxy *px = s->proxy;
3186 int status;
3187 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003188
Christopher Faulet61cc8522020-04-20 14:54:42 +02003189 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003190
Christopher Faulet61cc8522020-04-20 14:54:42 +02003191 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003192
Christopher Faulet61cc8522020-04-20 14:54:42 +02003193 pid = fork();
3194 if (pid < 0) {
3195 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3196 (global.tune.options & GTUNE_INSECURE_FORK) ?
3197 "" : " (likely caused by missing 'insecure-fork-wanted')",
3198 strerror(errno));
3199 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003200 goto out;
3201 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003202 if (pid == 0) {
3203 /* Child */
3204 extern char **environ;
3205 struct rlimit limit;
3206 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003207
Christopher Faulet61cc8522020-04-20 14:54:42 +02003208 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3209 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003210
Christopher Faulet61cc8522020-04-20 14:54:42 +02003211 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003212
Christopher Faulet61cc8522020-04-20 14:54:42 +02003213 /* restore the initial FD limits */
3214 limit.rlim_cur = rlim_fd_cur_at_boot;
3215 limit.rlim_max = rlim_fd_max_at_boot;
3216 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3217 getrlimit(RLIMIT_NOFILE, &limit);
3218 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3219 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3220 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3221 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003222
Christopher Faulet61cc8522020-04-20 14:54:42 +02003223 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003224
Christopher Faulet61cc8522020-04-20 14:54:42 +02003225 /* Update some environment variables and command args: curconn, server addr and server port */
3226 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003227
Christopher Faulet61cc8522020-04-20 14:54:42 +02003228 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3229 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003230
Christopher Faulet61cc8522020-04-20 14:54:42 +02003231 *check->argv[4] = 0;
3232 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3233 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3234 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003235
Christopher Faulet61cc8522020-04-20 14:54:42 +02003236 haproxy_unblock_signals();
3237 execvp(px->check_command, check->argv);
3238 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3239 strerror(errno));
3240 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003241 }
3242
Christopher Faulet61cc8522020-04-20 14:54:42 +02003243 /* Parent */
3244 if (check->result == CHK_RES_UNKNOWN) {
3245 if (pid_list_add(pid, t) != NULL) {
3246 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3247
3248 if (px->timeout.check && px->timeout.connect) {
3249 int t_con = tick_add(now_ms, px->timeout.connect);
3250 t->expire = tick_first(t->expire, t_con);
3251 }
3252 status = SF_ERR_NONE;
3253 goto out;
3254 }
3255 else {
3256 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3257 }
3258 kill(pid, SIGTERM); /* process creation error */
3259 }
3260 else
3261 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3262
3263out:
3264 unblock_sigchld();
3265 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003266}
3267
Christopher Faulet61cc8522020-04-20 14:54:42 +02003268/*
3269 * manages a server health-check that uses an external process. Returns
3270 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003271 *
3272 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003273 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003274 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003275static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003276{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003277 struct check *check = context;
3278 struct server *s = check->server;
3279 int rv;
3280 int ret;
3281 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003282
Christopher Faulet61cc8522020-04-20 14:54:42 +02003283 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3284 if (!(check->state & CHK_ST_INPROGRESS)) {
3285 /* no check currently running */
3286 if (!expired) /* woke up too early */
3287 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003288
Christopher Faulet61cc8522020-04-20 14:54:42 +02003289 /* we don't send any health-checks when the proxy is
3290 * stopped, the server should not be checked or the check
3291 * is disabled.
3292 */
3293 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3294 s->proxy->state == PR_STSTOPPED)
3295 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003296
Christopher Faulet61cc8522020-04-20 14:54:42 +02003297 /* we'll initiate a new check */
3298 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003299
Christopher Faulet61cc8522020-04-20 14:54:42 +02003300 check->state |= CHK_ST_INPROGRESS;
3301
3302 ret = connect_proc_chk(t);
3303 if (ret == SF_ERR_NONE) {
3304 /* the process was forked, we allow up to min(inter,
3305 * timeout.connect) for it to report its status, but
3306 * only when timeout.check is set as it may be to short
3307 * for a full check otherwise.
3308 */
3309 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3310
3311 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3312 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3313 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003314 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003315 task_set_affinity(t, tid_bit);
3316 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003317 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003318
Christopher Faulet61cc8522020-04-20 14:54:42 +02003319 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003320
Christopher Faulet61cc8522020-04-20 14:54:42 +02003321 check->state &= ~CHK_ST_INPROGRESS;
3322 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003323
Christopher Faulet61cc8522020-04-20 14:54:42 +02003324 /* we allow up to min(inter, timeout.connect) for a connection
3325 * to establish but only when timeout.check is set
3326 * as it may be to short for a full check otherwise
3327 */
3328 while (tick_is_expired(t->expire, now_ms)) {
3329 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003330
Christopher Faulet61cc8522020-04-20 14:54:42 +02003331 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3332 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003333
Christopher Faulet61cc8522020-04-20 14:54:42 +02003334 if (s->proxy->timeout.check)
3335 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003336 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003337 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003338 else {
3339 /* there was a test running.
3340 * First, let's check whether there was an uncaught error,
3341 * which can happen on connect timeout or error.
3342 */
3343 if (check->result == CHK_RES_UNKNOWN) {
3344 /* good connection is enough for pure TCP check */
3345 struct pid_list *elem = check->curpid;
3346 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003347
Christopher Faulet61cc8522020-04-20 14:54:42 +02003348 if (elem->exited) {
3349 status = elem->status; /* Save in case the process exits between use below */
3350 if (!WIFEXITED(status))
3351 check->code = -1;
3352 else
3353 check->code = WEXITSTATUS(status);
3354 if (!WIFEXITED(status) || WEXITSTATUS(status))
3355 status = HCHK_STATUS_PROCERR;
3356 else
3357 status = HCHK_STATUS_PROCOK;
3358 } else if (expired) {
3359 status = HCHK_STATUS_PROCTOUT;
3360 ha_warning("kill %d\n", (int)elem->pid);
3361 kill(elem->pid, SIGTERM);
3362 }
3363 set_server_check_status(check, status, NULL);
3364 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003365
Christopher Faulet61cc8522020-04-20 14:54:42 +02003366 if (check->result == CHK_RES_FAILED) {
3367 /* a failure or timeout detected */
3368 check_notify_failure(check);
3369 }
3370 else if (check->result == CHK_RES_CONDPASS) {
3371 /* check is OK but asks for stopping mode */
3372 check_notify_stopping(check);
3373 }
3374 else if (check->result == CHK_RES_PASSED) {
3375 /* a success was detected */
3376 check_notify_success(check);
3377 }
3378 task_set_affinity(t, 1);
3379 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003380
Christopher Faulet61cc8522020-04-20 14:54:42 +02003381 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003382
Christopher Faulet61cc8522020-04-20 14:54:42 +02003383 rv = 0;
3384 if (global.spread_checks > 0) {
3385 rv = srv_getinter(check) * global.spread_checks / 100;
3386 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3387 }
3388 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3389 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003390
Christopher Faulet61cc8522020-04-20 14:54:42 +02003391 reschedule:
3392 while (tick_is_expired(t->expire, now_ms))
3393 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003394
Christopher Faulet61cc8522020-04-20 14:54:42 +02003395 out_unlock:
3396 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3397 return t;
3398}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003399
Baptiste Assmann248f1172018-03-01 21:49:01 +01003400
Christopher Faulet61cc8522020-04-20 14:54:42 +02003401/**************************************************************************/
3402/***************** Health-checks based on connections *********************/
3403/**************************************************************************/
3404/* This function is used only for server health-checks. It handles connection
3405 * status updates including errors. If necessary, it wakes the check task up.
3406 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3407 * connection (eg: reconnect). It relies on tcpcheck_main().
3408 */
3409static int wake_srv_chk(struct conn_stream *cs)
3410{
3411 struct connection *conn = cs->conn;
3412 struct check *check = cs->data;
3413 struct email_alertq *q = container_of(check, typeof(*q), check);
3414 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003415
Christopher Faulet61cc8522020-04-20 14:54:42 +02003416 if (check->server)
3417 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3418 else
3419 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003420
Christopher Faulet61cc8522020-04-20 14:54:42 +02003421 /* we may have to make progress on the TCP checks */
3422 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003423
Christopher Faulet61cc8522020-04-20 14:54:42 +02003424 cs = check->cs;
3425 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003426
Christopher Faulet61cc8522020-04-20 14:54:42 +02003427 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3428 /* We may get error reports bypassing the I/O handlers, typically
3429 * the case when sending a pure TCP check which fails, then the I/O
3430 * handlers above are not called. This is completely handled by the
3431 * main processing task so let's simply wake it up. If we get here,
3432 * we expect errno to still be valid.
3433 */
3434 chk_report_conn_err(check, errno, 0);
3435 task_wakeup(check->task, TASK_WOKEN_IO);
3436 }
3437
3438 if (check->result != CHK_RES_UNKNOWN) {
3439 /* Check complete or aborted. If connection not yet closed do it
3440 * now and wake the check task up to be sure the result is
3441 * handled ASAP. */
3442 conn_sock_drain(conn);
3443 cs_close(cs);
3444 ret = -1;
3445 /* We may have been scheduled to run, and the
3446 * I/O handler expects to have a cs, so remove
3447 * the tasklet
3448 */
3449 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3450 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003451 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003452
3453 if (check->server)
3454 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003455 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003456 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003457
Christopher Faulet61cc8522020-04-20 14:54:42 +02003458 /* if a connection got replaced, we must absolutely prevent the connection
3459 * handler from touching its fd, and perform the FD polling updates ourselves
3460 */
3461 if (ret < 0)
3462 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003463
Christopher Faulet61cc8522020-04-20 14:54:42 +02003464 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003465}
3466
Christopher Faulet61cc8522020-04-20 14:54:42 +02003467/* This function checks if any I/O is wanted, and if so, attempts to do so */
3468static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003469{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003470 struct check *check = ctx;
3471 struct conn_stream *cs = check->cs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003472
Christopher Faulet3d5e1212020-05-28 14:34:02 +02003473 wake_srv_chk(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003474 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003475}
3476
Christopher Faulet61cc8522020-04-20 14:54:42 +02003477/* manages a server health-check that uses a connection. Returns
3478 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3479 *
3480 * Please do NOT place any return statement in this function and only leave
3481 * via the out_unlock label.
3482 */
3483static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003484{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003485 struct check *check = context;
3486 struct proxy *proxy = check->proxy;
3487 struct conn_stream *cs = check->cs;
3488 struct connection *conn = cs_conn(cs);
3489 int rv;
3490 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003491
Christopher Faulet61cc8522020-04-20 14:54:42 +02003492 if (check->server)
3493 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3494 if (!(check->state & CHK_ST_INPROGRESS)) {
3495 /* no check currently running */
3496 if (!expired) /* woke up too early */
3497 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003498
Christopher Faulet61cc8522020-04-20 14:54:42 +02003499 /* we don't send any health-checks when the proxy is
3500 * stopped, the server should not be checked or the check
3501 * is disabled.
3502 */
3503 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3504 proxy->state == PR_STSTOPPED)
3505 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003506
Christopher Faulet61cc8522020-04-20 14:54:42 +02003507 /* we'll initiate a new check */
3508 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003509
Christopher Faulet61cc8522020-04-20 14:54:42 +02003510 check->state |= CHK_ST_INPROGRESS;
3511 b_reset(&check->bi);
3512 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003513
Christopher Faulet61cc8522020-04-20 14:54:42 +02003514 task_set_affinity(t, tid_bit);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003515
Christopher Faulet99ff1052020-05-25 07:32:01 +02003516 check->current_step = NULL;
3517 tcpcheck_main(check);
3518 goto out_unlock;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003519 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003520 else {
3521 /* there was a test running.
3522 * First, let's check whether there was an uncaught error,
3523 * which can happen on connect timeout or error.
3524 */
3525 if (check->result == CHK_RES_UNKNOWN) {
3526 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3527 chk_report_conn_err(check, 0, expired);
3528 }
3529 else
3530 goto out_unlock; /* timeout not reached, wait again */
3531 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003532
Christopher Faulet61cc8522020-04-20 14:54:42 +02003533 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003534
Christopher Faulet61cc8522020-04-20 14:54:42 +02003535 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003536
Christopher Faulet61cc8522020-04-20 14:54:42 +02003537 if (conn && conn->xprt) {
3538 /* The check was aborted and the connection was not yet closed.
3539 * This can happen upon timeout, or when an external event such
3540 * as a failed response coupled with "observe layer7" caused the
3541 * server state to be suddenly changed.
3542 */
3543 conn_sock_drain(conn);
3544 cs_close(cs);
3545 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003546
Christopher Faulet61cc8522020-04-20 14:54:42 +02003547 if (cs) {
3548 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003549 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003550 /* We may have been scheduled to run, and the
3551 * I/O handler expects to have a cs, so remove
3552 * the tasklet
3553 */
3554 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3555 cs_destroy(cs);
3556 cs = check->cs = NULL;
3557 conn = NULL;
3558 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003559
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003560 if (check->sess != NULL) {
3561 vars_prune(&check->vars, check->sess, NULL);
3562 session_free(check->sess);
3563 check->sess = NULL;
3564 }
3565
Christopher Faulet61cc8522020-04-20 14:54:42 +02003566 if (check->server) {
3567 if (check->result == CHK_RES_FAILED) {
3568 /* a failure or timeout detected */
3569 check_notify_failure(check);
3570 }
3571 else if (check->result == CHK_RES_CONDPASS) {
3572 /* check is OK but asks for stopping mode */
3573 check_notify_stopping(check);
3574 }
3575 else if (check->result == CHK_RES_PASSED) {
3576 /* a success was detected */
3577 check_notify_success(check);
3578 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003579 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003580 task_set_affinity(t, MAX_THREADS_MASK);
3581 check->state &= ~CHK_ST_INPROGRESS;
3582
3583 if (check->server) {
3584 rv = 0;
3585 if (global.spread_checks > 0) {
3586 rv = srv_getinter(check) * global.spread_checks / 100;
3587 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3588 }
3589 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003590 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003591 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003592
Christopher Faulet61cc8522020-04-20 14:54:42 +02003593 reschedule:
3594 while (tick_is_expired(t->expire, now_ms))
3595 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3596 out_unlock:
3597 if (check->server)
3598 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3599 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003600}
3601
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003602
Christopher Faulet61cc8522020-04-20 14:54:42 +02003603/**************************************************************************/
3604/******************* Internals to parse tcp-check rules *******************/
3605/**************************************************************************/
3606struct action_kw_list tcp_check_keywords = {
3607 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3608};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003609
Christopher Faulet61cc8522020-04-20 14:54:42 +02003610/* Return the struct action_kw associated to a keyword */
3611static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003612{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003613 return action_lookup(&tcp_check_keywords.list, kw);
3614}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003615
Christopher Faulet61cc8522020-04-20 14:54:42 +02003616static void action_kw_tcp_check_build_list(struct buffer *chk)
3617{
3618 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003619}
3620
Christopher Faulet61cc8522020-04-20 14:54:42 +02003621/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3622 * returned on error.
3623 */
3624static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3625 struct list *rules, struct action_kw *kw,
3626 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003627{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003628 struct tcpcheck_rule *chk = NULL;
3629 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003630
Christopher Faulet61cc8522020-04-20 14:54:42 +02003631 actrule = calloc(1, sizeof(*actrule));
3632 if (!actrule) {
3633 memprintf(errmsg, "out of memory");
3634 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003635 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003636 actrule->kw = kw;
3637 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003638
Christopher Faulet61cc8522020-04-20 14:54:42 +02003639 cur_arg++;
3640 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3641 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3642 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003643 }
3644
Christopher Faulet61cc8522020-04-20 14:54:42 +02003645 chk = calloc(1, sizeof(*chk));
3646 if (!chk) {
3647 memprintf(errmsg, "out of memory");
3648 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003649 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003650 chk->action = TCPCHK_ACT_ACTION_KW;
3651 chk->action_kw.rule = actrule;
3652 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003653
3654 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003655 free(actrule);
3656 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003657}
3658
Christopher Faulet61cc8522020-04-20 14:54:42 +02003659/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3660 * returned on error.
3661 */
3662static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3663 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003664{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003665 struct tcpcheck_rule *chk = NULL;
3666 struct sockaddr_storage *sk = NULL;
3667 char *comment = NULL, *sni = NULL, *alpn = NULL;
3668 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003669 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003670 unsigned short conn_opts = 0;
3671 long port = 0;
3672 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003673
Christopher Faulet61cc8522020-04-20 14:54:42 +02003674 list_for_each_entry(chk, rules, list) {
3675 if (chk->action == TCPCHK_ACT_CONNECT)
3676 break;
3677 if (chk->action == TCPCHK_ACT_COMMENT ||
3678 chk->action == TCPCHK_ACT_ACTION_KW ||
3679 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3680 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003681
Christopher Faulet61cc8522020-04-20 14:54:42 +02003682 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003683 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003684 "when there is a 'connect' step in the tcp-check ruleset");
3685 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003686 }
3687
Christopher Faulet61cc8522020-04-20 14:54:42 +02003688 cur_arg++;
3689 while (*(args[cur_arg])) {
3690 if (strcmp(args[cur_arg], "default") == 0)
3691 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3692 else if (strcmp(args[cur_arg], "addr") == 0) {
3693 int port1, port2;
3694 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003695
Christopher Faulet61cc8522020-04-20 14:54:42 +02003696 if (!*(args[cur_arg+1])) {
3697 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3698 goto error;
3699 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003700
Christopher Faulet61cc8522020-04-20 14:54:42 +02003701 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3702 if (!sk) {
3703 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3704 goto error;
3705 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003706
Christopher Faulet61cc8522020-04-20 14:54:42 +02003707 proto = protocol_by_family(sk->ss_family);
3708 if (!proto || !proto->connect) {
3709 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3710 args[cur_arg]);
3711 goto error;
3712 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003713
Christopher Faulet61cc8522020-04-20 14:54:42 +02003714 if (port1 != port2) {
3715 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3716 args[cur_arg], args[cur_arg+1]);
3717 goto error;
3718 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003719
Christopher Faulet61cc8522020-04-20 14:54:42 +02003720 cur_arg++;
3721 }
3722 else if (strcmp(args[cur_arg], "port") == 0) {
3723 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003724
Christopher Faulet61cc8522020-04-20 14:54:42 +02003725 if (!*(args[cur_arg+1])) {
3726 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3727 goto error;
3728 }
3729 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003730
Christopher Faulet61cc8522020-04-20 14:54:42 +02003731 port = 0;
3732 release_sample_expr(port_expr);
3733 p = args[cur_arg]; end = p + strlen(p);
3734 port = read_uint(&p, end);
3735 if (p != end) {
3736 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003737
Christopher Faulet61cc8522020-04-20 14:54:42 +02003738 px->conf.args.ctx = ARGC_SRV;
3739 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3740 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003741
Christopher Faulet61cc8522020-04-20 14:54:42 +02003742 if (!port_expr) {
3743 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3744 goto error;
3745 }
3746 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3747 memprintf(errmsg, "error detected while parsing port expression : "
3748 " fetch method '%s' extracts information from '%s', "
3749 "none of which is available here.\n",
3750 args[cur_arg], sample_src_names(port_expr->fetch->use));
3751 goto error;
3752 }
3753 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3754 }
3755 else if (port > 65535 || port < 1) {
3756 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3757 args[cur_arg]);
3758 goto error;
3759 }
3760 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003761 else if (strcmp(args[cur_arg], "proto") == 0) {
3762 if (!*(args[cur_arg+1])) {
3763 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3764 goto error;
3765 }
3766 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3767 if (!mux_proto) {
3768 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3769 goto error;
3770 }
3771 cur_arg++;
3772 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003773 else if (strcmp(args[cur_arg], "comment") == 0) {
3774 if (!*(args[cur_arg+1])) {
3775 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3776 goto error;
3777 }
3778 cur_arg++;
3779 free(comment);
3780 comment = strdup(args[cur_arg]);
3781 if (!comment) {
3782 memprintf(errmsg, "out of memory");
3783 goto error;
3784 }
3785 }
3786 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3787 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3788 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3789 conn_opts |= TCPCHK_OPT_SOCKS4;
3790 else if (strcmp(args[cur_arg], "linger") == 0)
3791 conn_opts |= TCPCHK_OPT_LINGER;
3792#ifdef USE_OPENSSL
3793 else if (strcmp(args[cur_arg], "ssl") == 0) {
3794 px->options |= PR_O_TCPCHK_SSL;
3795 conn_opts |= TCPCHK_OPT_SSL;
3796 }
3797 else if (strcmp(args[cur_arg], "sni") == 0) {
3798 if (!*(args[cur_arg+1])) {
3799 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3800 goto error;
3801 }
3802 cur_arg++;
3803 free(sni);
3804 sni = strdup(args[cur_arg]);
3805 if (!sni) {
3806 memprintf(errmsg, "out of memory");
3807 goto error;
3808 }
3809 }
3810 else if (strcmp(args[cur_arg], "alpn") == 0) {
3811#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3812 free(alpn);
3813 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3814 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3815 goto error;
3816 }
3817 cur_arg++;
3818#else
3819 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003820 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003821#endif
3822 }
3823#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003824
Christopher Faulet61cc8522020-04-20 14:54:42 +02003825 else {
3826 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3827#ifdef USE_OPENSSL
3828 ", 'ssl', 'sni', 'alpn'"
3829#endif /* USE_OPENSSL */
3830 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3831 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003832 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003833 }
3834 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003835 }
3836
Christopher Faulet61cc8522020-04-20 14:54:42 +02003837 chk = calloc(1, sizeof(*chk));
3838 if (!chk) {
3839 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003840 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003841 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003842 chk->action = TCPCHK_ACT_CONNECT;
3843 chk->comment = comment;
3844 chk->connect.port = port;
3845 chk->connect.options = conn_opts;
3846 chk->connect.sni = sni;
3847 chk->connect.alpn = alpn;
3848 chk->connect.alpn_len= alpn_len;
3849 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003850 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003851 if (sk)
3852 chk->connect.addr = *sk;
3853 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003854
Christopher Faulet61cc8522020-04-20 14:54:42 +02003855 error:
3856 free(alpn);
3857 free(sni);
3858 free(comment);
3859 release_sample_expr(port_expr);
3860 return NULL;
3861}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003862
Christopher Faulet61cc8522020-04-20 14:54:42 +02003863/* Parses and creates a tcp-check send rule. NULL is returned on error */
3864static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3865 const char *file, int line, char **errmsg)
3866{
3867 struct tcpcheck_rule *chk = NULL;
3868 char *comment = NULL, *data = NULL;
3869 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003870
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003871 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3872 type = TCPCHK_SEND_BINARY_LF;
3873 else if (strcmp(args[cur_arg], "send-binary") == 0)
3874 type = TCPCHK_SEND_BINARY;
3875 else if (strcmp(args[cur_arg], "send-lf") == 0)
3876 type = TCPCHK_SEND_STRING_LF;
3877 else if (strcmp(args[cur_arg], "send") == 0)
3878 type = TCPCHK_SEND_STRING;
3879
Christopher Faulet61cc8522020-04-20 14:54:42 +02003880 if (!*(args[cur_arg+1])) {
3881 memprintf(errmsg, "'%s' expects a %s as argument",
3882 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003883 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003884 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003885
Christopher Faulet61cc8522020-04-20 14:54:42 +02003886 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003887
Christopher Faulet61cc8522020-04-20 14:54:42 +02003888 cur_arg += 2;
3889 while (*(args[cur_arg])) {
3890 if (strcmp(args[cur_arg], "comment") == 0) {
3891 if (!*(args[cur_arg+1])) {
3892 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3893 goto error;
3894 }
3895 cur_arg++;
3896 free(comment);
3897 comment = strdup(args[cur_arg]);
3898 if (!comment) {
3899 memprintf(errmsg, "out of memory");
3900 goto error;
3901 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003902 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003903 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003904 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003905 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003906 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003907 }
3908 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003909 }
3910
Christopher Faulet61cc8522020-04-20 14:54:42 +02003911 chk = calloc(1, sizeof(*chk));
3912 if (!chk) {
3913 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003914 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003915 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003916 chk->action = TCPCHK_ACT_SEND;
3917 chk->comment = comment;
3918 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003919
Christopher Faulet61cc8522020-04-20 14:54:42 +02003920 switch (chk->send.type) {
3921 case TCPCHK_SEND_STRING:
3922 chk->send.data = ist2(strdup(data), strlen(data));
3923 if (!isttest(chk->send.data)) {
3924 memprintf(errmsg, "out of memory");
3925 goto error;
3926 }
3927 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003928 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003929 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003930 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003931 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3932 goto error;
3933 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003934 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003935 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003936 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003937 case TCPCHK_SEND_STRING_LF:
3938 case TCPCHK_SEND_BINARY_LF:
3939 LIST_INIT(&chk->send.fmt);
3940 px->conf.args.ctx = ARGC_SRV;
3941 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3942 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3943 goto error;
3944 }
3945 break;
3946 case TCPCHK_SEND_HTTP:
3947 case TCPCHK_SEND_UNDEF:
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 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003952
Christopher Faulet61cc8522020-04-20 14:54:42 +02003953 error:
3954 free(chk);
3955 free(comment);
3956 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003957}
3958
Christopher Faulet61cc8522020-04-20 14:54:42 +02003959/* Parses and creates a http-check send rule. NULL is returned on error */
3960static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3961 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003962{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003963 struct tcpcheck_rule *chk = NULL;
3964 struct tcpcheck_http_hdr *hdr = NULL;
3965 struct http_hdr hdrs[global.tune.max_http_hdr];
3966 char *meth = NULL, *uri = NULL, *vsn = NULL;
3967 char *body = NULL, *comment = NULL;
3968 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003969 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003970
3971 cur_arg++;
3972 while (*(args[cur_arg])) {
3973 if (strcmp(args[cur_arg], "meth") == 0) {
3974 if (!*(args[cur_arg+1])) {
3975 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3976 goto error;
3977 }
3978 cur_arg++;
3979 meth = args[cur_arg];
3980 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003981 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003982 if (!*(args[cur_arg+1])) {
3983 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3984 goto error;
3985 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003986 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
3987 if (strcmp(args[cur_arg], "uri-lf") == 0)
3988 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003989 cur_arg++;
3990 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02003991 }
Christopher Faulet907701b2020-04-28 09:37:00 +02003992 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003993 if (!*(args[cur_arg+1])) {
3994 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3995 goto error;
3996 }
3997 cur_arg++;
3998 vsn = args[cur_arg];
3999 }
4000 else if (strcmp(args[cur_arg], "hdr") == 0) {
4001 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4002 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4003 goto error;
4004 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004005
4006 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4007 if (host_hdr >= 0) {
4008 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4009 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4010 goto error;
4011 }
4012 host_hdr = i;
4013 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004014 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4015 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4016 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4017 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004018
Christopher Faulet61cc8522020-04-20 14:54:42 +02004019 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4020 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4021 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004022 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004023 cur_arg += 2;
4024 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004025 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004026 if (!*(args[cur_arg+1])) {
4027 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4028 goto error;
4029 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004030 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4031 if (strcmp(args[cur_arg], "body-lf") == 0)
4032 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004033 cur_arg++;
4034 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004035 }
4036 else if (strcmp(args[cur_arg], "comment") == 0) {
4037 if (!*(args[cur_arg+1])) {
4038 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4039 goto error;
4040 }
4041 cur_arg++;
4042 free(comment);
4043 comment = strdup(args[cur_arg]);
4044 if (!comment) {
4045 memprintf(errmsg, "out of memory");
4046 goto error;
4047 }
4048 }
4049 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004050 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004051 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004052 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004053 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004054 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004055 }
4056
Christopher Faulet61cc8522020-04-20 14:54:42 +02004057 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004058
Christopher Faulet61cc8522020-04-20 14:54:42 +02004059 chk = calloc(1, sizeof(*chk));
4060 if (!chk) {
4061 memprintf(errmsg, "out of memory");
4062 goto error;
4063 }
4064 chk->action = TCPCHK_ACT_SEND;
4065 chk->comment = comment; comment = NULL;
4066 chk->send.type = TCPCHK_SEND_HTTP;
4067 chk->send.http.flags = flags;
4068 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004069
Christopher Faulet61cc8522020-04-20 14:54:42 +02004070 if (meth) {
4071 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4072 chk->send.http.meth.str.area = strdup(meth);
4073 chk->send.http.meth.str.data = strlen(meth);
4074 if (!chk->send.http.meth.str.area) {
4075 memprintf(errmsg, "out of memory");
4076 goto error;
4077 }
4078 }
4079 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004080 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4081 LIST_INIT(&chk->send.http.uri_fmt);
4082 px->conf.args.ctx = ARGC_SRV;
4083 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4084 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4085 goto error;
4086 }
4087 }
4088 else {
4089 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4090 if (!isttest(chk->send.http.uri)) {
4091 memprintf(errmsg, "out of memory");
4092 goto error;
4093 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004094 }
4095 }
4096 if (vsn) {
4097 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4098 if (!isttest(chk->send.http.vsn)) {
4099 memprintf(errmsg, "out of memory");
4100 goto error;
4101 }
4102 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004103 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004104 hdr = calloc(1, sizeof(*hdr));
4105 if (!hdr) {
4106 memprintf(errmsg, "out of memory");
4107 goto error;
4108 }
4109 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004110 hdr->name = istdup(hdrs[i].n);
4111 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004112 memprintf(errmsg, "out of memory");
4113 goto error;
4114 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004115
Christopher Fauletb61caf42020-04-21 10:57:42 +02004116 ist0(hdrs[i].v);
4117 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 +02004118 goto error;
4119 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4120 hdr = NULL;
4121 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004122
Christopher Faulet61cc8522020-04-20 14:54:42 +02004123 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004124 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4125 LIST_INIT(&chk->send.http.body_fmt);
4126 px->conf.args.ctx = ARGC_SRV;
4127 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4128 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4129 goto error;
4130 }
4131 }
4132 else {
4133 chk->send.http.body = ist2(strdup(body), strlen(body));
4134 if (!isttest(chk->send.http.body)) {
4135 memprintf(errmsg, "out of memory");
4136 goto error;
4137 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004138 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004139 }
4140
Christopher Faulet61cc8522020-04-20 14:54:42 +02004141 return chk;
4142
4143 error:
4144 free_tcpcheck_http_hdr(hdr);
4145 free_tcpcheck(chk, 0);
4146 free(comment);
4147 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004148}
4149
Christopher Faulet61cc8522020-04-20 14:54:42 +02004150/* Parses and creates a http-check comment rule. NULL is returned on error */
4151static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4152 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004153{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004154 struct tcpcheck_rule *chk = NULL;
4155 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004156
Christopher Faulet61cc8522020-04-20 14:54:42 +02004157 if (!*(args[cur_arg+1])) {
4158 memprintf(errmsg, "expects a string as argument");
4159 goto error;
4160 }
4161 cur_arg++;
4162 comment = strdup(args[cur_arg]);
4163 if (!comment) {
4164 memprintf(errmsg, "out of memory");
4165 goto error;
4166 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004167
Christopher Faulet61cc8522020-04-20 14:54:42 +02004168 chk = calloc(1, sizeof(*chk));
4169 if (!chk) {
4170 memprintf(errmsg, "out of memory");
4171 goto error;
4172 }
4173 chk->action = TCPCHK_ACT_COMMENT;
4174 chk->comment = comment;
4175 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004176
Christopher Faulet61cc8522020-04-20 14:54:42 +02004177 error:
4178 free(comment);
4179 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004180}
4181
Christopher Faulet61cc8522020-04-20 14:54:42 +02004182/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4183 * on error. <proto> is set to the right protocol flags (covered by the
4184 * TCPCHK_RULES_PROTO_CHK mask).
4185 */
4186static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4187 struct list *rules, unsigned int proto,
4188 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004189{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004190 struct tcpcheck_rule *prev_check, *chk = NULL;
4191 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004192 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004193 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004194 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4195 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4196 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004197 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004198 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004199 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004200
Christopher Faulet39708192020-05-05 10:47:36 +02004201 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004202 if (!*(args[cur_arg+1])) {
4203 memprintf(errmsg, "expects at least a matching pattern as arguments");
4204 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004205 }
4206
Christopher Faulet61cc8522020-04-20 14:54:42 +02004207 cur_arg++;
4208 while (*(args[cur_arg])) {
4209 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004210
Christopher Faulet61cc8522020-04-20 14:54:42 +02004211 rescan:
4212 if (strcmp(args[cur_arg], "min-recv") == 0) {
4213 if (in_pattern) {
4214 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4215 goto error;
4216 }
4217 if (!*(args[cur_arg+1])) {
4218 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4219 goto error;
4220 }
4221 /* Use an signed integer here because of chksize */
4222 cur_arg++;
4223 min_recv = atol(args[cur_arg]);
4224 if (min_recv < -1 || min_recv > INT_MAX) {
4225 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4226 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004227 }
4228 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004229 else if (*(args[cur_arg]) == '!') {
4230 in_pattern = 1;
4231 while (*(args[cur_arg]) == '!') {
4232 inverse = !inverse;
4233 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004234 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004235 if (!*(args[cur_arg]))
4236 cur_arg++;
4237 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004238 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004239 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4240 if (type != TCPCHK_EXPECT_UNDEF) {
4241 memprintf(errmsg, "only on pattern expected");
4242 goto error;
4243 }
4244 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004245 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004246 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004247 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004248
Christopher Faulet61cc8522020-04-20 14:54:42 +02004249 if (!*(args[cur_arg+1])) {
4250 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4251 goto error;
4252 }
4253 cur_arg++;
4254 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004255 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004256 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4257 if (proto == TCPCHK_RULES_HTTP_CHK)
4258 goto bad_http_kw;
4259 if (type != TCPCHK_EXPECT_UNDEF) {
4260 memprintf(errmsg, "only on pattern expected");
4261 goto error;
4262 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004263 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004264
Christopher Faulet61cc8522020-04-20 14:54:42 +02004265 if (!*(args[cur_arg+1])) {
4266 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4267 goto error;
4268 }
4269 cur_arg++;
4270 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004271 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004272 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4273 if (type != TCPCHK_EXPECT_UNDEF) {
4274 memprintf(errmsg, "only on pattern expected");
4275 goto error;
4276 }
4277 if (proto != TCPCHK_RULES_HTTP_CHK)
4278 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4279 else {
4280 if (*(args[cur_arg]) != 's')
4281 goto bad_http_kw;
4282 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4283 }
4284
4285 if (!*(args[cur_arg+1])) {
4286 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4287 goto error;
4288 }
4289 cur_arg++;
4290 pattern = args[cur_arg];
4291 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004292 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4293 if (proto != TCPCHK_RULES_HTTP_CHK)
4294 goto bad_tcp_kw;
4295 if (type != TCPCHK_EXPECT_UNDEF) {
4296 memprintf(errmsg, "only on pattern expected");
4297 goto error;
4298 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004299 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004300
Christopher Faulet61cc8522020-04-20 14:54:42 +02004301 if (!*(args[cur_arg+1])) {
4302 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4303 goto error;
4304 }
4305 cur_arg++;
4306 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004307 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004308 else if (strcmp(args[cur_arg], "custom") == 0) {
4309 if (in_pattern) {
4310 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4311 goto error;
4312 }
4313 if (type != TCPCHK_EXPECT_UNDEF) {
4314 memprintf(errmsg, "only on pattern expected");
4315 goto error;
4316 }
4317 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004318 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004319 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004320 int orig_arg = cur_arg;
4321
4322 if (proto != TCPCHK_RULES_HTTP_CHK)
4323 goto bad_tcp_kw;
4324 if (type != TCPCHK_EXPECT_UNDEF) {
4325 memprintf(errmsg, "only on pattern expected");
4326 goto error;
4327 }
4328 type = TCPCHK_EXPECT_HTTP_HEADER;
4329
Christopher Fauletb5594262020-05-05 20:23:13 +02004330 if (strcmp(args[cur_arg], "fhdr") == 0)
4331 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4332
Christopher Faulet39708192020-05-05 10:47:36 +02004333 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004334 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4335 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4336 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004337 args[orig_arg]);
4338 goto error;
4339 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004340
4341 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4342 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4343
Christopher Faulet39708192020-05-05 10:47:36 +02004344 cur_arg += 2;
4345 if (strcmp(args[cur_arg], "-m") == 0) {
4346 if (!*(args[cur_arg+1])) {
4347 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4348 args[orig_arg], args[cur_arg]);
4349 goto error;
4350 }
4351 if (strcmp(args[cur_arg+1], "str") == 0)
4352 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4353 else if (strcmp(args[cur_arg+1], "beg") == 0)
4354 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4355 else if (strcmp(args[cur_arg+1], "end") == 0)
4356 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4357 else if (strcmp(args[cur_arg+1], "sub") == 0)
4358 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004359 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4360 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4361 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4362 args[orig_arg]);
4363 goto error;
4364 }
Christopher Faulet39708192020-05-05 10:47:36 +02004365 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004366 }
Christopher Faulet39708192020-05-05 10:47:36 +02004367 else {
4368 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4369 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4370 goto error;
4371 }
4372 cur_arg += 2;
4373 }
4374 else
4375 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4376 npat = args[cur_arg];
4377
Christopher Fauletb5594262020-05-05 20:23:13 +02004378 if (!*(args[cur_arg+1]) ||
4379 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004380 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4381 goto next;
4382 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004383 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4384 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004385
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004386 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004387 if (strcmp(args[cur_arg+2], "-m") == 0) {
4388 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004389 if (!*(args[cur_arg+1])) {
4390 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4391 args[orig_arg], args[cur_arg]);
4392 goto error;
4393 }
4394 if (strcmp(args[cur_arg+1], "str") == 0)
4395 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4396 else if (strcmp(args[cur_arg+1], "beg") == 0)
4397 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4398 else if (strcmp(args[cur_arg+1], "end") == 0)
4399 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4400 else if (strcmp(args[cur_arg+1], "sub") == 0)
4401 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004402 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4403 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4404 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4405 args[orig_arg]);
4406 goto error;
4407 }
Christopher Faulet39708192020-05-05 10:47:36 +02004408 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004409 }
Christopher Faulet39708192020-05-05 10:47:36 +02004410 else {
4411 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4412 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4413 goto error;
4414 }
Christopher Faulet39708192020-05-05 10:47:36 +02004415 }
4416 else
4417 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004418
Christopher Fauletb5594262020-05-05 20:23:13 +02004419 if (!*(args[cur_arg+2])) {
4420 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4421 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004422 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004423 vpat = args[cur_arg+2];
4424 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004425 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004426 else if (strcmp(args[cur_arg], "comment") == 0) {
4427 if (in_pattern) {
4428 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4429 goto error;
4430 }
4431 if (!*(args[cur_arg+1])) {
4432 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4433 goto error;
4434 }
4435 cur_arg++;
4436 free(comment);
4437 comment = strdup(args[cur_arg]);
4438 if (!comment) {
4439 memprintf(errmsg, "out of memory");
4440 goto error;
4441 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004442 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004443 else if (strcmp(args[cur_arg], "on-success") == 0) {
4444 if (in_pattern) {
4445 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4446 goto error;
4447 }
4448 if (!*(args[cur_arg+1])) {
4449 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4450 goto error;
4451 }
4452 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004453 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004454 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004455 else if (strcmp(args[cur_arg], "on-error") == 0) {
4456 if (in_pattern) {
4457 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4458 goto error;
4459 }
4460 if (!*(args[cur_arg+1])) {
4461 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4462 goto error;
4463 }
4464 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004465 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004466 }
4467 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4468 if (in_pattern) {
4469 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4470 goto error;
4471 }
4472 if (!*(args[cur_arg+1])) {
4473 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4474 goto error;
4475 }
4476 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4477 ok_st = HCHK_STATUS_L7OKD;
4478 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4479 ok_st = HCHK_STATUS_L7OKCD;
4480 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4481 ok_st = HCHK_STATUS_L6OK;
4482 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4483 ok_st = HCHK_STATUS_L4OK;
4484 else {
4485 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4486 args[cur_arg], args[cur_arg+1]);
4487 goto error;
4488 }
4489 cur_arg++;
4490 }
4491 else if (strcmp(args[cur_arg], "error-status") == 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 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4501 err_st = HCHK_STATUS_L7RSP;
4502 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4503 err_st = HCHK_STATUS_L7STS;
4504 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4505 err_st = HCHK_STATUS_L6RSP;
4506 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4507 err_st = HCHK_STATUS_L4CON;
4508 else {
4509 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4510 args[cur_arg], args[cur_arg+1]);
4511 goto error;
4512 }
4513 cur_arg++;
4514 }
4515 else if (strcmp(args[cur_arg], "status-code") == 0) {
4516 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004517
Christopher Faulet61cc8522020-04-20 14:54:42 +02004518 if (in_pattern) {
4519 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4520 goto error;
4521 }
4522 if (!*(args[cur_arg+1])) {
4523 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4524 goto error;
4525 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004526
Christopher Faulet61cc8522020-04-20 14:54:42 +02004527 cur_arg++;
4528 release_sample_expr(status_expr);
4529 px->conf.args.ctx = ARGC_SRV;
4530 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4531 file, line, errmsg, &px->conf.args, NULL);
4532 if (!status_expr) {
4533 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4534 goto error;
4535 }
4536 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4537 memprintf(errmsg, "error detected while parsing status-code expression : "
4538 " fetch method '%s' extracts information from '%s', "
4539 "none of which is available here.\n",
4540 args[cur_arg], sample_src_names(status_expr->fetch->use));
4541 goto error;
4542 }
4543 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4544 }
4545 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4546 if (in_pattern) {
4547 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4548 goto error;
4549 }
4550 if (!*(args[cur_arg+1])) {
4551 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4552 goto error;
4553 }
4554 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4555 tout_st = HCHK_STATUS_L7TOUT;
4556 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4557 tout_st = HCHK_STATUS_L6TOUT;
4558 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4559 tout_st = HCHK_STATUS_L4TOUT;
4560 else {
4561 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4562 args[cur_arg], args[cur_arg+1]);
4563 goto error;
4564 }
4565 cur_arg++;
4566 }
4567 else {
4568 if (proto == TCPCHK_RULES_HTTP_CHK) {
4569 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004570 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004571 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004572 }
4573 else {
4574 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004575 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4576 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004577 }
4578 goto error;
4579 }
Christopher Faulet39708192020-05-05 10:47:36 +02004580 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004581 cur_arg++;
4582 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004583
Christopher Faulet61cc8522020-04-20 14:54:42 +02004584 chk = calloc(1, sizeof(*chk));
4585 if (!chk) {
4586 memprintf(errmsg, "out of memory");
4587 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004588 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004589 chk->action = TCPCHK_ACT_EXPECT;
4590 LIST_INIT(&chk->expect.onerror_fmt);
4591 LIST_INIT(&chk->expect.onsuccess_fmt);
4592 chk->comment = comment; comment = NULL;
4593 chk->expect.type = type;
4594 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004595 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004596 chk->expect.ok_status = ok_st;
4597 chk->expect.err_status = err_st;
4598 chk->expect.tout_status = tout_st;
4599 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004600
Christopher Faulet61cc8522020-04-20 14:54:42 +02004601 if (on_success_msg) {
4602 px->conf.args.ctx = ARGC_SRV;
4603 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4604 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4605 goto error;
4606 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004607 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004608 if (on_error_msg) {
4609 px->conf.args.ctx = ARGC_SRV;
4610 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4611 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4612 goto error;
4613 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004614 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004615
Christopher Faulet61cc8522020-04-20 14:54:42 +02004616 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004617 case TCPCHK_EXPECT_HTTP_STATUS: {
4618 const char *p = pattern;
4619 unsigned int c1,c2;
4620
4621 chk->expect.codes.codes = NULL;
4622 chk->expect.codes.num = 0;
4623 while (1) {
4624 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4625 if (*p == '-') {
4626 p++;
4627 c2 = read_uint(&p, pattern + strlen(pattern));
4628 }
4629 if (c1 > c2) {
4630 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4631 goto error;
4632 }
4633
4634 chk->expect.codes.num++;
4635 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4636 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4637 if (!chk->expect.codes.codes) {
4638 memprintf(errmsg, "out of memory");
4639 goto error;
4640 }
4641 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4642 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4643
4644 if (*p == '\0')
4645 break;
4646 if (*p != ',') {
4647 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4648 goto error;
4649 }
4650 p++;
4651 }
4652 break;
4653 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004654 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004655 case TCPCHK_EXPECT_HTTP_BODY:
4656 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004657 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004658 memprintf(errmsg, "out of memory");
4659 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004660 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004661 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004662 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004663 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004664
4665 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004666 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4667 goto error;
4668 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004669 chk->expect.data.len = len;
4670 break;
4671 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004672 case TCPCHK_EXPECT_STRING_REGEX:
4673 case TCPCHK_EXPECT_BINARY_REGEX:
4674 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4675 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004676 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004677 if (!chk->expect.regex)
4678 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004679 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004680
4681 case TCPCHK_EXPECT_STRING_LF:
4682 case TCPCHK_EXPECT_BINARY_LF:
4683 case TCPCHK_EXPECT_HTTP_BODY_LF:
4684 LIST_INIT(&chk->expect.fmt);
4685 px->conf.args.ctx = ARGC_SRV;
4686 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4687 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4688 goto error;
4689 }
4690 break;
4691
Christopher Faulet39708192020-05-05 10:47:36 +02004692 case TCPCHK_EXPECT_HTTP_HEADER:
4693 if (!npat) {
4694 memprintf(errmsg, "unexpected error, undefined header name pattern");
4695 goto error;
4696 }
4697 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4698 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4699 if (!chk->expect.hdr.name_re)
4700 goto error;
4701 }
4702 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4703 px->conf.args.ctx = ARGC_SRV;
4704 LIST_INIT(&chk->expect.hdr.name_fmt);
4705 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4706 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4707 goto error;
4708 }
4709 }
4710 else {
4711 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4712 if (!isttest(chk->expect.hdr.name)) {
4713 memprintf(errmsg, "out of memory");
4714 goto error;
4715 }
4716 }
4717
4718 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4719 chk->expect.hdr.value = IST_NULL;
4720 break;
4721 }
4722
4723 if (!vpat) {
4724 memprintf(errmsg, "unexpected error, undefined header value pattern");
4725 goto error;
4726 }
4727 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4728 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4729 if (!chk->expect.hdr.value_re)
4730 goto error;
4731 }
4732 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4733 px->conf.args.ctx = ARGC_SRV;
4734 LIST_INIT(&chk->expect.hdr.value_fmt);
4735 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4736 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4737 goto error;
4738 }
4739 }
4740 else {
4741 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4742 if (!isttest(chk->expect.hdr.value)) {
4743 memprintf(errmsg, "out of memory");
4744 goto error;
4745 }
4746 }
4747
Christopher Faulet61cc8522020-04-20 14:54:42 +02004748 break;
4749 case TCPCHK_EXPECT_CUSTOM:
4750 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4751 break;
4752 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004753 memprintf(errmsg, "pattern not found");
4754 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004755 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004756
Christopher Faulet61cc8522020-04-20 14:54:42 +02004757 /* All tcp-check expect points back to the first inverse expect rule in
4758 * a chain of one or more expect rule, potentially itself.
4759 */
4760 chk->expect.head = chk;
4761 list_for_each_entry_rev(prev_check, rules, list) {
4762 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4763 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4764 chk->expect.head = prev_check;
4765 continue;
4766 }
4767 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4768 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004769 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004770 return chk;
4771
4772 error:
4773 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004774 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004775 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004776 return NULL;
4777}
4778
Christopher Faulet61cc8522020-04-20 14:54:42 +02004779/* Overwrites fields of the old http send rule with those of the new one. When
4780 * replaced, old values are freed and replaced by the new ones. New values are
4781 * not copied but transferred. At the end <new> should be empty and can be
4782 * safely released. This function never fails.
4783 */
4784static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004785{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004786 struct logformat_node *lf, *lfb;
4787 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004788
Christopher Faulet404f9192020-04-09 23:13:54 +02004789
Christopher Faulet61cc8522020-04-20 14:54:42 +02004790 if (new->send.http.meth.str.area) {
4791 free(old->send.http.meth.str.area);
4792 old->send.http.meth.meth = new->send.http.meth.meth;
4793 old->send.http.meth.str.area = new->send.http.meth.str.area;
4794 old->send.http.meth.str.data = new->send.http.meth.str.data;
4795 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004796 }
4797
Christopher Faulet61cc8522020-04-20 14:54:42 +02004798 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4799 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004800 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004801 else
4802 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4803 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4804 old->send.http.uri = new->send.http.uri;
4805 new->send.http.uri = IST_NULL;
4806 }
4807 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4808 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004809 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004810 else
4811 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4812 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4813 LIST_INIT(&old->send.http.uri_fmt);
4814 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4815 LIST_DEL(&lf->list);
4816 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4817 }
4818 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004819
Christopher Faulet61cc8522020-04-20 14:54:42 +02004820 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004821 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004822 old->send.http.vsn = new->send.http.vsn;
4823 new->send.http.vsn = IST_NULL;
4824 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004825
Christopher Faulet61cc8522020-04-20 14:54:42 +02004826 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4827 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4828 LIST_DEL(&hdr->list);
4829 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004830 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004831
4832 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4833 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004834 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004835 else
4836 free_tcpcheck_fmt(&old->send.http.body_fmt);
4837 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4838 old->send.http.body = new->send.http.body;
4839 new->send.http.body = IST_NULL;
4840 }
4841 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4842 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004843 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004844 else
4845 free_tcpcheck_fmt(&old->send.http.body_fmt);
4846 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4847 LIST_INIT(&old->send.http.body_fmt);
4848 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4849 LIST_DEL(&lf->list);
4850 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4851 }
4852 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004853}
4854
Christopher Faulet61cc8522020-04-20 14:54:42 +02004855/* Internal function used to add an http-check rule in a list during the config
4856 * parsing step. Depending on its type, and the previously inserted rules, a
4857 * specific action may be performed or an error may be reported. This functions
4858 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4859 * message.
4860 */
4861static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004862{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004863 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004864
Christopher Faulet61cc8522020-04-20 14:54:42 +02004865 /* the implicit send rule coming from an "option httpchk" line must be
4866 * merged with the first explici http-check send rule, if
4867 * any. Depdending the declaration order some tests are required.
4868 *
4869 * Some tests is also required for other kinds of http-check rules to be
4870 * sure the ruleset remains valid.
4871 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004872
Christopher Faulet61cc8522020-04-20 14:54:42 +02004873 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004874 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004875 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4876 * following tests are performed :
4877 *
4878 * 1- If there is no such rule or if it is not a send rule, the implicit send
4879 * rule is pushed in front of the ruleset
4880 *
4881 * 2- If it is another implicit send rule, it is replaced with the new one.
4882 *
4883 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004884 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004885 * new send rule (the implicit one).
4886 */
4887 r = get_first_tcpcheck_rule(rules);
4888 if (r && r->action == TCPCHK_ACT_CONNECT)
4889 r = get_next_tcpcheck_rule(rules, r);
4890 if (!r || r->action != TCPCHK_ACT_SEND)
4891 LIST_ADD(rules->list, &chk->list);
4892 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4893 LIST_DEL(&r->list);
4894 free_tcpcheck(r, 0);
4895 LIST_ADD(rules->list, &chk->list);
4896 }
4897 else {
4898 tcpcheck_overwrite_send_http_rule(r, chk);
4899 free_tcpcheck(chk, 0);
4900 }
4901 }
4902 else {
4903 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4904 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4905 * with an existing implicit send rule, if any. At the end, if there is no error,
4906 * the rule is appended to the list.
4907 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004908
Christopher Faulet61cc8522020-04-20 14:54:42 +02004909 r = get_last_tcpcheck_rule(rules);
4910 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4911 /* no error */;
4912 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4913 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4914 chk->index+1);
4915 return 0;
4916 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004917 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004918 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4919 chk->index+1);
4920 return 0;
4921 }
4922 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4923 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4924 chk->index+1);
4925 return 0;
4926 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004927
Christopher Faulet61cc8522020-04-20 14:54:42 +02004928 if (chk->action == TCPCHK_ACT_SEND) {
4929 r = get_first_tcpcheck_rule(rules);
4930 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4931 tcpcheck_overwrite_send_http_rule(r, chk);
4932 free_tcpcheck(chk, 0);
4933 LIST_DEL(&r->list);
4934 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4935 chk = r;
4936 }
4937 }
4938 LIST_ADDQ(rules->list, &chk->list);
4939 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004940 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004941}
4942
Christopher Faulet61cc8522020-04-20 14:54:42 +02004943/**************************************************************************/
4944/************************** Init/deinit checks ****************************/
4945/**************************************************************************/
4946static const char *init_check(struct check *check, int type)
4947{
4948 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004949
Christopher Faulet61cc8522020-04-20 14:54:42 +02004950 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4951 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004952
Christopher Faulet61cc8522020-04-20 14:54:42 +02004953 check->bi.area = calloc(check->bi.size, sizeof(char));
4954 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004955
Christopher Faulet61cc8522020-04-20 14:54:42 +02004956 if (!check->bi.area || !check->bo.area)
4957 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004958
Christopher Faulet61cc8522020-04-20 14:54:42 +02004959 check->wait_list.tasklet = tasklet_new();
4960 if (!check->wait_list.tasklet)
4961 return "out of memory while allocating check tasklet";
4962 check->wait_list.events = 0;
4963 check->wait_list.tasklet->process = event_srv_chk_io;
4964 check->wait_list.tasklet->context = check;
4965 return NULL;
4966}
4967
4968void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004969{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004970 task_destroy(check->task);
4971 if (check->wait_list.tasklet)
4972 tasklet_free(check->wait_list.tasklet);
4973
4974 free(check->bi.area);
4975 free(check->bo.area);
4976 if (check->cs) {
4977 free(check->cs->conn);
4978 check->cs->conn = NULL;
4979 cs_free(check->cs);
4980 check->cs = NULL;
4981 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004982}
4983
Christopher Faulet61cc8522020-04-20 14:54:42 +02004984/* manages a server health-check. Returns the time the task accepts to wait, or
4985 * TIME_ETERNITY for infinity.
4986 */
4987static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004988{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004989 struct check *check = context;
4990
4991 if (check->type == PR_O2_EXT_CHK)
4992 return process_chk_proc(t, context, state);
4993 return process_chk_conn(t, context, state);
4994
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004995}
4996
Christopher Faulet61cc8522020-04-20 14:54:42 +02004997
4998static int start_check_task(struct check *check, int mininter,
4999 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005000{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005001 struct task *t;
5002 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005003
Christopher Faulet61cc8522020-04-20 14:54:42 +02005004 if (check->type == PR_O2_EXT_CHK)
5005 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005006
Christopher Faulet61cc8522020-04-20 14:54:42 +02005007 /* task for the check */
5008 if ((t = task_new(thread_mask)) == NULL) {
5009 ha_alert("Starting [%s:%s] check: out of memory.\n",
5010 check->server->proxy->id, check->server->id);
5011 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005012 }
5013
Christopher Faulet61cc8522020-04-20 14:54:42 +02005014 check->task = t;
5015 t->process = process_chk;
5016 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005017
Christopher Faulet61cc8522020-04-20 14:54:42 +02005018 if (mininter < srv_getinter(check))
5019 mininter = srv_getinter(check);
5020
5021 if (global.max_spread_checks && mininter > global.max_spread_checks)
5022 mininter = global.max_spread_checks;
5023
5024 /* check this every ms */
5025 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5026 check->start = now;
5027 task_queue(t);
5028
5029 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005030}
5031
Christopher Faulet61cc8522020-04-20 14:54:42 +02005032/* updates the server's weight during a warmup stage. Once the final weight is
5033 * reached, the task automatically stops. Note that any server status change
5034 * must have updated s->last_change accordingly.
5035 */
5036static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005037{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005038 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005039
Christopher Faulet61cc8522020-04-20 14:54:42 +02005040 /* by default, plan on stopping the task */
5041 t->expire = TICK_ETERNITY;
5042 if ((s->next_admin & SRV_ADMF_MAINT) ||
5043 (s->next_state != SRV_ST_STARTING))
5044 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005045
Christopher Faulet61cc8522020-04-20 14:54:42 +02005046 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005047
Christopher Faulet61cc8522020-04-20 14:54:42 +02005048 /* recalculate the weights and update the state */
5049 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005050
Christopher Faulet61cc8522020-04-20 14:54:42 +02005051 /* probably that we can refill this server with a bit more connections */
5052 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005053
Christopher Faulet61cc8522020-04-20 14:54:42 +02005054 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005055
Christopher Faulet61cc8522020-04-20 14:54:42 +02005056 /* get back there in 1 second or 1/20th of the slowstart interval,
5057 * whichever is greater, resulting in small 5% steps.
5058 */
5059 if (s->next_state == SRV_ST_STARTING)
5060 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5061 return t;
5062}
5063
5064/*
5065 * Start health-check.
5066 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5067 */
5068static int start_checks()
5069{
5070
5071 struct proxy *px;
5072 struct server *s;
5073 struct task *t;
5074 int nbcheck=0, mininter=0, srvpos=0;
5075
5076 /* 0- init the dummy frontend used to create all checks sessions */
5077 init_new_proxy(&checks_fe);
5078 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5079 checks_fe.mode = PR_MODE_TCP;
5080 checks_fe.maxconn = 0;
5081 checks_fe.conn_retries = CONN_RETRIES;
5082 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5083 checks_fe.timeout.client = TICK_ETERNITY;
5084
5085 /* 1- count the checkers to run simultaneously.
5086 * We also determine the minimum interval among all of those which
5087 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5088 * will be used to spread their start-up date. Those which have
5089 * a shorter interval will start independently and will not dictate
5090 * too short an interval for all others.
5091 */
5092 for (px = proxies_list; px; px = px->next) {
5093 for (s = px->srv; s; s = s->next) {
5094 if (s->slowstart) {
5095 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5096 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5097 return ERR_ALERT | ERR_FATAL;
5098 }
5099 /* We need a warmup task that will be called when the server
5100 * state switches from down to up.
5101 */
5102 s->warmup = t;
5103 t->process = server_warmup;
5104 t->context = s;
5105 /* server can be in this state only because of */
5106 if (s->next_state == SRV_ST_STARTING)
5107 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 +02005108 }
5109
Christopher Faulet61cc8522020-04-20 14:54:42 +02005110 if (s->check.state & CHK_ST_CONFIGURED) {
5111 nbcheck++;
5112 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5113 (!mininter || mininter > srv_getinter(&s->check)))
5114 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005115 }
5116
Christopher Faulet61cc8522020-04-20 14:54:42 +02005117 if (s->agent.state & CHK_ST_CONFIGURED) {
5118 nbcheck++;
5119 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5120 (!mininter || mininter > srv_getinter(&s->agent)))
5121 mininter = srv_getinter(&s->agent);
5122 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005123 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005124 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005125
Christopher Faulet61cc8522020-04-20 14:54:42 +02005126 if (!nbcheck)
5127 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005128
Christopher Faulet61cc8522020-04-20 14:54:42 +02005129 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005130
Christopher Faulet61cc8522020-04-20 14:54:42 +02005131 /*
5132 * 2- start them as far as possible from each others. For this, we will
5133 * start them after their interval set to the min interval divided by
5134 * the number of servers, weighted by the server's position in the list.
5135 */
5136 for (px = proxies_list; px; px = px->next) {
5137 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5138 if (init_pid_list()) {
5139 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5140 return ERR_ALERT | ERR_FATAL;
5141 }
5142 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005143
Christopher Faulet61cc8522020-04-20 14:54:42 +02005144 for (s = px->srv; s; s = s->next) {
5145 /* A task for the main check */
5146 if (s->check.state & CHK_ST_CONFIGURED) {
5147 if (s->check.type == PR_O2_EXT_CHK) {
5148 if (!prepare_external_check(&s->check))
5149 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005150 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005151 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5152 return ERR_ALERT | ERR_FATAL;
5153 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005154 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005155
Christopher Faulet61cc8522020-04-20 14:54:42 +02005156 /* A task for a auxiliary agent check */
5157 if (s->agent.state & CHK_ST_CONFIGURED) {
5158 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5159 return ERR_ALERT | ERR_FATAL;
5160 }
5161 srvpos++;
5162 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005163 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005164 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005165 return 0;
5166}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005167
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005168
Christopher Faulet61cc8522020-04-20 14:54:42 +02005169/*
5170 * Return value:
5171 * the port to be used for the health check
5172 * 0 in case no port could be found for the check
5173 */
5174static int srv_check_healthcheck_port(struct check *chk)
5175{
5176 int i = 0;
5177 struct server *srv = NULL;
5178
5179 srv = chk->server;
5180
5181 /* by default, we use the health check port ocnfigured */
5182 if (chk->port > 0)
5183 return chk->port;
5184
5185 /* try to get the port from check_core.addr if check.port not set */
5186 i = get_host_port(&chk->addr);
5187 if (i > 0)
5188 return i;
5189
5190 /* try to get the port from server address */
5191 /* prevent MAPPORTS from working at this point, since checks could
5192 * not be performed in such case (MAPPORTS impose a relative ports
5193 * based on live traffic)
5194 */
5195 if (srv->flags & SRV_F_MAPPORTS)
5196 return 0;
5197
5198 i = srv->svc_port; /* by default */
5199 if (i > 0)
5200 return i;
5201
5202 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005203}
5204
Christopher Faulet61cc8522020-04-20 14:54:42 +02005205/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5206 * if an error occurred.
5207 */
5208static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005209{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005210 const char *err;
5211 struct tcpcheck_rule *r;
5212 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005213
Christopher Faulet61cc8522020-04-20 14:54:42 +02005214 if (!srv->do_check)
5215 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005216
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005217
Christopher Faulet61cc8522020-04-20 14:54:42 +02005218 /* If neither a port nor an addr was specified and no check transport
5219 * layer is forced, then the transport layer used by the checks is the
5220 * same as for the production traffic. Otherwise we use raw_sock by
5221 * default, unless one is specified.
5222 */
5223 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5224 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5225 srv->check.use_ssl = srv->use_ssl;
5226 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005227 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005228 else if (srv->check.use_ssl == 1)
5229 srv->check.xprt = xprt_get(XPRT_SSL);
5230 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005231 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02005232 else if (srv->check.use_ssl == 1)
5233 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005234
Christopher Faulet12882cf2020-04-23 15:50:18 +02005235 /* Inherit the mux protocol from the server if not already defined for
5236 * the check
5237 */
5238 if (srv->mux_proto && !srv->check.mux_proto)
5239 srv->check.mux_proto = srv->mux_proto;
5240
Christopher Faulet61cc8522020-04-20 14:54:42 +02005241 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005242
Christopher Faulet61cc8522020-04-20 14:54:42 +02005243 /* We need at least a service port, a check port or the first tcp-check
5244 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5245 */
5246 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5247 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5248 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005249
Christopher Faulet61cc8522020-04-20 14:54:42 +02005250 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5251 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5252 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5253 ret |= ERR_ALERT | ERR_ABORT;
5254 goto out;
5255 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005256
Christopher Faulet61cc8522020-04-20 14:54:42 +02005257 /* search the first action (connect / send / expect) in the list */
5258 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5259 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5260 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5261 "nor tcp_check rule 'connect' with port information.\n",
5262 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5263 ret |= ERR_ALERT | ERR_ABORT;
5264 goto out;
5265 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005266
Christopher Faulet61cc8522020-04-20 14:54:42 +02005267 /* scan the tcp-check ruleset to ensure a port has been configured */
5268 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5269 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5270 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5271 "and a tcp_check rule 'connect' with no port information.\n",
5272 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5273 ret |= ERR_ALERT | ERR_ABORT;
5274 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005275 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005276 }
5277
Christopher Faulet61cc8522020-04-20 14:54:42 +02005278 init:
5279 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5280 struct tcpcheck_ruleset *rs = NULL;
5281 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5282 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005283
Christopher Faulet61cc8522020-04-20 14:54:42 +02005284 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5285 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005286
Christopher Faulet61cc8522020-04-20 14:54:42 +02005287 rs = find_tcpcheck_ruleset("*tcp-check");
5288 if (!rs) {
5289 rs = create_tcpcheck_ruleset("*tcp-check");
5290 if (rs == NULL) {
5291 ha_alert("config: %s '%s': out of memory.\n",
5292 proxy_type_str(srv->proxy), srv->proxy->id);
5293 ret |= ERR_ALERT | ERR_FATAL;
5294 goto out;
5295 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005296 }
5297
Christopher Faulet61cc8522020-04-20 14:54:42 +02005298 free_tcpcheck_vars(&rules->preset_vars);
5299 rules->list = &rs->rules;
5300 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005301 }
5302
Christopher Faulet61cc8522020-04-20 14:54:42 +02005303 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5304 if (err) {
5305 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5306 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5307 ret |= ERR_ALERT | ERR_ABORT;
5308 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005309 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005310 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5311 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005312
Christopher Faulet61cc8522020-04-20 14:54:42 +02005313 out:
5314 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005315}
5316
Christopher Faulet61cc8522020-04-20 14:54:42 +02005317/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5318 * if an error occurred.
5319 */
5320static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005321{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005322 struct tcpcheck_rule *chk;
5323 const char *err;
5324 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005325
Christopher Faulet61cc8522020-04-20 14:54:42 +02005326 if (!srv->do_agent)
5327 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005328
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005329 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005330 * implicit one is inserted before all others.
5331 */
5332 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5333 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5334 chk = calloc(1, sizeof(*chk));
5335 if (!chk) {
5336 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5337 " to agent-check for server '%s' (out of memory).\n",
5338 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5339 ret |= ERR_ALERT | ERR_FATAL;
5340 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005341 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005342 chk->action = TCPCHK_ACT_CONNECT;
5343 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5344 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005345 }
5346
Christopher Faulete5870d82020-04-15 11:32:03 +02005347
Christopher Faulet61cc8522020-04-20 14:54:42 +02005348 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5349 if (err) {
5350 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5351 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5352 ret |= ERR_ALERT | ERR_ABORT;
5353 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005354 }
5355
Christopher Faulet61cc8522020-04-20 14:54:42 +02005356 if (!srv->agent.inter)
5357 srv->agent.inter = srv->check.inter;
5358
5359 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5360 global.maxsock++;
5361
5362 out:
5363 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005364}
5365
Christopher Faulet61cc8522020-04-20 14:54:42 +02005366/* Check tcp-check health-check configuration for the proxy <px>. */
5367static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005368{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005369 struct tcpcheck_rule *chk, *back;
5370 char *comment = NULL, *errmsg = NULL;
5371 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5372 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005373
Christopher Faulet61cc8522020-04-20 14:54:42 +02005374 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5375 deinit_proxy_tcpcheck(px);
5376 goto out;
5377 }
5378
5379 free(px->check_command);
5380 free(px->check_path);
5381 px->check_command = px->check_path = NULL;
5382
5383 if (!px->tcpcheck_rules.list) {
5384 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5385 ret |= ERR_ALERT | ERR_FATAL;
5386 goto out;
5387 }
5388
5389 /* HTTP ruleset only : */
5390 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5391 struct tcpcheck_rule *next;
5392
5393 /* move remaining implicit send rule from "option httpchk" line to the right place.
5394 * If such rule exists, it must be the first one. In this case, the rule is moved
5395 * after the first connect rule, if any. Otherwise, nothing is done.
5396 */
5397 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5398 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5399 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5400 if (next && next->action == TCPCHK_ACT_CONNECT) {
5401 LIST_DEL(&chk->list);
5402 LIST_ADD(&next->list, &chk->list);
5403 chk->index = next->index;
5404 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005405 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005406
5407 /* add implicit expect rule if the last one is a send. It is inherited from previous
5408 * versions where the http expect rule was optional. Now it is possible to chained
5409 * send/expect rules but the last expect may still be implicit.
5410 */
5411 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5412 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005413 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005414 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5415 px->conf.file, px->conf.line, &errmsg);
5416 if (!next) {
5417 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5418 "(%s).\n", px->id, errmsg);
5419 free(errmsg);
5420 ret |= ERR_ALERT | ERR_FATAL;
5421 goto out;
5422 }
5423 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5424 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005425 }
5426 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005427
5428 /* For all ruleset: */
5429
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005430 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005431 * implicit one is inserted before all others.
5432 */
5433 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5434 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5435 chk = calloc(1, sizeof(*chk));
5436 if (!chk) {
5437 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5438 "(out of memory).\n", px->id);
5439 ret |= ERR_ALERT | ERR_FATAL;
5440 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005441 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005442 chk->action = TCPCHK_ACT_CONNECT;
5443 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5444 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5445 }
5446
5447 /* Remove all comment rules. To do so, when a such rule is found, the
5448 * comment is assigned to the following rule(s).
5449 */
5450 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5451 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5452 free(comment);
5453 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005454 }
5455
Christopher Faulet61cc8522020-04-20 14:54:42 +02005456 prev_action = chk->action;
5457 switch (chk->action) {
5458 case TCPCHK_ACT_COMMENT:
5459 free(comment);
5460 comment = chk->comment;
5461 LIST_DEL(&chk->list);
5462 free(chk);
5463 break;
5464 case TCPCHK_ACT_CONNECT:
5465 if (!chk->comment && comment)
5466 chk->comment = strdup(comment);
5467 /* fall though */
5468 case TCPCHK_ACT_ACTION_KW:
5469 free(comment);
5470 comment = NULL;
5471 break;
5472 case TCPCHK_ACT_SEND:
5473 case TCPCHK_ACT_EXPECT:
5474 if (!chk->comment && comment)
5475 chk->comment = strdup(comment);
5476 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005477 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005478 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005479 free(comment);
5480 comment = NULL;
5481
5482 out:
5483 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005484}
5485
Christopher Faulet61cc8522020-04-20 14:54:42 +02005486void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005487{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005488 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5489 px->tcpcheck_rules.flags = 0;
5490 px->tcpcheck_rules.list = NULL;
5491}
Christopher Faulete5870d82020-04-15 11:32:03 +02005492
Christopher Faulet61cc8522020-04-20 14:54:42 +02005493static void deinit_srv_check(struct server *srv)
5494{
5495 if (srv->check.state & CHK_ST_CONFIGURED)
5496 free_check(&srv->check);
5497 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5498 srv->do_check = 0;
5499}
Christopher Faulete5870d82020-04-15 11:32:03 +02005500
Christopher Faulet61cc8522020-04-20 14:54:42 +02005501
5502static void deinit_srv_agent_check(struct server *srv)
5503{
5504 if (srv->agent.tcpcheck_rules) {
5505 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5506 free(srv->agent.tcpcheck_rules);
5507 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005508 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005509
Christopher Faulet61cc8522020-04-20 14:54:42 +02005510 if (srv->agent.state & CHK_ST_CONFIGURED)
5511 free_check(&srv->agent);
5512
5513 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5514 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005515}
5516
Christopher Faulet61cc8522020-04-20 14:54:42 +02005517static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005518{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005519 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005520 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005521 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005522
Christopher Fauletd7cee712020-04-21 13:45:00 +02005523 node = ebpt_first(&shared_tcpchecks);
5524 while (node) {
5525 next = ebpt_next(node);
5526 ebpt_delete(node);
5527 free(node->key);
5528 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005529 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5530 LIST_DEL(&r->list);
5531 free_tcpcheck(r, 0);
5532 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005533 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005534 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005535 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005536}
Christopher Faulete5870d82020-04-15 11:32:03 +02005537
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005538
Christopher Faulet61cc8522020-04-20 14:54:42 +02005539REGISTER_POST_SERVER_CHECK(init_srv_check);
5540REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5541REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5542REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005543
Christopher Faulet61cc8522020-04-20 14:54:42 +02005544REGISTER_SERVER_DEINIT(deinit_srv_check);
5545REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5546REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5547REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005548
Christopher Faulet61cc8522020-04-20 14:54:42 +02005549/**************************************************************************/
5550/****************************** Email alerts ******************************/
5551/* NOTE: It may be pertinent to use an applet to handle email alerts */
5552/* instead of a tcp-check ruleset */
5553/**************************************************************************/
5554void email_alert_free(struct email_alert *alert)
5555{
5556 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005557
Christopher Faulet61cc8522020-04-20 14:54:42 +02005558 if (!alert)
5559 return;
5560
5561 if (alert->rules.list) {
5562 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5563 LIST_DEL(&rule->list);
5564 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005565 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005566 free_tcpcheck_vars(&alert->rules.preset_vars);
5567 free(alert->rules.list);
5568 alert->rules.list = NULL;
5569 }
5570 pool_free(pool_head_email_alert, alert);
5571}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005572
Christopher Faulet61cc8522020-04-20 14:54:42 +02005573static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5574{
5575 struct check *check = context;
5576 struct email_alertq *q;
5577 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005578
Christopher Faulet61cc8522020-04-20 14:54:42 +02005579 q = container_of(check, typeof(*q), check);
5580
5581 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5582 while (1) {
5583 if (!(check->state & CHK_ST_ENABLED)) {
5584 if (LIST_ISEMPTY(&q->email_alerts)) {
5585 /* All alerts processed, queue the task */
5586 t->expire = TICK_ETERNITY;
5587 task_queue(t);
5588 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005589 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005590
5591 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5592 LIST_DEL(&alert->list);
5593 t->expire = now_ms;
5594 check->tcpcheck_rules = &alert->rules;
5595 check->status = HCHK_STATUS_INI;
5596 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005597 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005598
5599 process_chk(t, context, state);
5600 if (check->state & CHK_ST_INPROGRESS)
5601 break;
5602
5603 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5604 email_alert_free(alert);
5605 check->tcpcheck_rules = NULL;
5606 check->server = NULL;
5607 check->state &= ~CHK_ST_ENABLED;
5608 }
5609 end:
5610 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5611 return t;
5612}
5613
5614/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5615 *
5616 * The function returns 1 in success case, otherwise, it returns 0 and err is
5617 * filled.
5618 */
5619int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5620{
5621 struct mailer *mailer;
5622 struct email_alertq *queues;
5623 const char *err_str;
5624 int i = 0;
5625
5626 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5627 memprintf(err, "out of memory while allocating mailer alerts queues");
5628 goto fail_no_queue;
5629 }
5630
5631 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5632 struct email_alertq *q = &queues[i];
5633 struct check *check = &q->check;
5634 struct task *t;
5635
5636 LIST_INIT(&q->email_alerts);
5637 HA_SPIN_INIT(&q->lock);
5638 check->inter = mls->timeout.mail;
5639 check->rise = DEF_AGENT_RISETIME;
5640 check->proxy = p;
5641 check->fall = DEF_AGENT_FALLTIME;
5642 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5643 memprintf(err, "%s", err_str);
5644 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005645 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005646
5647 check->xprt = mailer->xprt;
5648 check->addr = mailer->addr;
5649 check->port = get_host_port(&mailer->addr);
5650
5651 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5652 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005653 goto error;
5654 }
5655
Christopher Faulet61cc8522020-04-20 14:54:42 +02005656 check->task = t;
5657 t->process = process_email_alert;
5658 t->context = check;
5659
5660 /* check this in one ms */
5661 t->expire = TICK_ETERNITY;
5662 check->start = now;
5663 task_queue(t);
5664 }
5665
5666 mls->users++;
5667 free(p->email_alert.mailers.name);
5668 p->email_alert.mailers.m = mls;
5669 p->email_alert.queues = queues;
5670 return 0;
5671
5672 error:
5673 for (i = 0; i < mls->count; i++) {
5674 struct email_alertq *q = &queues[i];
5675 struct check *check = &q->check;
5676
5677 free_check(check);
5678 }
5679 free(queues);
5680 fail_no_queue:
5681 return 1;
5682}
5683
5684static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5685{
5686 struct tcpcheck_rule *tcpcheck, *prev_check;
5687 struct tcpcheck_expect *expect;
5688
5689 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5690 return 0;
5691 memset(tcpcheck, 0, sizeof(*tcpcheck));
5692 tcpcheck->action = TCPCHK_ACT_EXPECT;
5693
5694 expect = &tcpcheck->expect;
5695 expect->type = TCPCHK_EXPECT_STRING;
5696 LIST_INIT(&expect->onerror_fmt);
5697 LIST_INIT(&expect->onsuccess_fmt);
5698 expect->ok_status = HCHK_STATUS_L7OKD;
5699 expect->err_status = HCHK_STATUS_L7RSP;
5700 expect->tout_status = HCHK_STATUS_L7TOUT;
5701 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005702 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005703 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5704 return 0;
5705 }
5706
5707 /* All tcp-check expect points back to the first inverse expect rule
5708 * in a chain of one or more expect rule, potentially itself.
5709 */
5710 tcpcheck->expect.head = tcpcheck;
5711 list_for_each_entry_rev(prev_check, rules->list, list) {
5712 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5713 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5714 tcpcheck->expect.head = prev_check;
5715 continue;
5716 }
5717 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5718 break;
5719 }
5720 LIST_ADDQ(rules->list, &tcpcheck->list);
5721 return 1;
5722}
5723
5724static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5725{
5726 struct tcpcheck_rule *tcpcheck;
5727 struct tcpcheck_send *send;
5728 const char *in;
5729 char *dst;
5730 int i;
5731
5732 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5733 return 0;
5734 memset(tcpcheck, 0, sizeof(*tcpcheck));
5735 tcpcheck->action = TCPCHK_ACT_SEND;
5736
5737 send = &tcpcheck->send;
5738 send->type = TCPCHK_SEND_STRING;
5739
5740 for (i = 0; strs[i]; i++)
5741 send->data.len += strlen(strs[i]);
5742
Christopher Fauletb61caf42020-04-21 10:57:42 +02005743 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005744 if (!isttest(send->data)) {
5745 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5746 return 0;
5747 }
5748
Christopher Fauletb61caf42020-04-21 10:57:42 +02005749 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005750 for (i = 0; strs[i]; i++)
5751 for (in = strs[i]; (*dst = *in++); dst++);
5752 *dst = 0;
5753
5754 LIST_ADDQ(rules->list, &tcpcheck->list);
5755 return 1;
5756}
5757
5758static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5759 struct email_alertq *q, const char *msg)
5760{
5761 struct email_alert *alert;
5762 struct tcpcheck_rule *tcpcheck;
5763 struct check *check = &q->check;
5764
5765 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5766 goto error;
5767 LIST_INIT(&alert->list);
5768 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5769 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5770 if (!alert->rules.list)
5771 goto error;
5772 LIST_INIT(alert->rules.list);
5773 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5774 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005775
Christopher Faulet61cc8522020-04-20 14:54:42 +02005776 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5777 goto error;
5778 memset(tcpcheck, 0, sizeof(*tcpcheck));
5779 tcpcheck->action = TCPCHK_ACT_CONNECT;
5780 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005781
Christopher Faulet61cc8522020-04-20 14:54:42 +02005782 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005783
Christopher Faulet61cc8522020-04-20 14:54:42 +02005784 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005785 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005786
5787 {
5788 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5789 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5790 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005791 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005792
Christopher Faulet61cc8522020-04-20 14:54:42 +02005793 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5794 goto error;
5795
5796 {
5797 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5798 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005799 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005800 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005801
5802 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5803 goto error;
5804
5805 {
5806 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5807 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005808 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005809 }
5810
Christopher Faulet61cc8522020-04-20 14:54:42 +02005811 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5812 goto error;
5813
5814 {
5815 const char * const strs[2] = { "DATA\r\n" };
5816 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005817 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005818 }
5819
5820 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5821 goto error;
5822
5823 {
5824 struct tm tm;
5825 char datestr[48];
5826 const char * const strs[18] = {
5827 "From: ", p->email_alert.from, "\r\n",
5828 "To: ", p->email_alert.to, "\r\n",
5829 "Date: ", datestr, "\r\n",
5830 "Subject: [HAproxy Alert] ", msg, "\r\n",
5831 "\r\n",
5832 msg, "\r\n",
5833 "\r\n",
5834 ".\r\n",
5835 NULL
5836 };
5837
5838 get_localtime(date.tv_sec, &tm);
5839
5840 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005841 goto error;
5842 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005843
5844 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005845 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005846 }
5847
5848 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005849 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005850
5851 {
5852 const char * const strs[2] = { "QUIT\r\n" };
5853 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5854 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005855 }
5856
Christopher Faulet61cc8522020-04-20 14:54:42 +02005857 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5858 goto error;
5859
5860 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5861 task_wakeup(check->task, TASK_WOKEN_MSG);
5862 LIST_ADDQ(&q->email_alerts, &alert->list);
5863 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5864 return 1;
5865
5866error:
5867 email_alert_free(alert);
5868 return 0;
5869}
5870
5871static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5872{
5873 int i;
5874 struct mailer *mailer;
5875
5876 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5877 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5878 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5879 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5880 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005881 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005882 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005883
Christopher Faulet61cc8522020-04-20 14:54:42 +02005884 return;
5885}
5886
5887/*
5888 * Send email alert if configured.
5889 */
5890void send_email_alert(struct server *s, int level, const char *format, ...)
5891{
5892 va_list argp;
5893 char buf[1024];
5894 int len;
5895 struct proxy *p = s->proxy;
5896
5897 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5898 return;
5899
5900 va_start(argp, format);
5901 len = vsnprintf(buf, sizeof(buf), format, argp);
5902 va_end(argp);
5903
5904 if (len < 0 || len >= sizeof(buf)) {
5905 ha_alert("Email alert [%s] could not format message\n", p->id);
5906 return;
5907 }
5908
5909 enqueue_email_alert(p, s, buf);
5910}
5911
5912/**************************************************************************/
5913/************************** Check sample fetches **************************/
5914/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005915
Christopher Faulet61cc8522020-04-20 14:54:42 +02005916static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005917 { /* END */ },
5918}};
5919
5920INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5921
5922
5923/**************************************************************************/
5924/************************ Check's parsing functions ***********************/
5925/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005926/* Parses the "tcp-check" proxy keyword */
5927static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5928 struct proxy *defpx, const char *file, int line,
5929 char **errmsg)
5930{
Christopher Faulet404f9192020-04-09 23:13:54 +02005931 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005932 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005933 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005934
5935 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5936 ret = 1;
5937
Christopher Faulet404f9192020-04-09 23:13:54 +02005938 /* Deduce the ruleset name from the proxy info */
5939 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5940 ((curpx == defpx) ? "defaults" : curpx->id),
5941 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005942
Christopher Faulet61cc8522020-04-20 14:54:42 +02005943 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005944 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005945 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005946 if (rs == NULL) {
5947 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005948 goto error;
5949 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005950 }
5951
Gaetan Rivet5301b012020-02-25 17:19:17 +01005952 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005953 if (!LIST_ISEMPTY(&rs->rules)) {
5954 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005955 index = chk->index + 1;
5956 }
5957
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005958 cur_arg = 1;
5959 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005960 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005961 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5962 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005963 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005964 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005965 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005966 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005967 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005968 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005969 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5970
5971 if (!kw) {
5972 action_kw_tcp_check_build_list(&trash);
5973 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5974 "%s%s. but got '%s'",
5975 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5976 goto error;
5977 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005978 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005979 }
5980
5981 if (!chk) {
5982 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5983 goto error;
5984 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005985 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005986
5987 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005988 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005989 LIST_ADDQ(&rs->rules, &chk->list);
5990
5991 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005992 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005993 /* Use this ruleset if the proxy already has tcp-check enabled */
5994 curpx->tcpcheck_rules.list = &rs->rules;
5995 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5996 }
5997 else {
5998 /* mark this ruleset as unused for now */
5999 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
6000 }
6001
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006002 return ret;
6003
6004 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006005 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006006 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006007 return -1;
6008}
6009
Christopher Faulet51b129f2020-04-09 15:54:18 +02006010/* Parses the "http-check" proxy keyword */
6011static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6012 struct proxy *defpx, const char *file, int line,
6013 char **errmsg)
6014{
Christopher Faulete5870d82020-04-15 11:32:03 +02006015 struct tcpcheck_ruleset *rs = NULL;
6016 struct tcpcheck_rule *chk = NULL;
6017 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006018
6019 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6020 ret = 1;
6021
6022 cur_arg = 1;
6023 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6024 /* enable a graceful server shutdown on an HTTP 404 response */
6025 curpx->options |= PR_O_DISABLE404;
6026 if (too_many_args(1, args, errmsg, NULL))
6027 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006028 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006029 }
6030 else if (strcmp(args[cur_arg], "send-state") == 0) {
6031 /* enable emission of the apparent state of a server in HTTP checks */
6032 curpx->options2 |= PR_O2_CHK_SNDST;
6033 if (too_many_args(1, args, errmsg, NULL))
6034 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006035 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006036 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006037
Christopher Faulete5870d82020-04-15 11:32:03 +02006038 /* Deduce the ruleset name from the proxy info */
6039 chunk_printf(&trash, "*http-check-%s_%s-%d",
6040 ((curpx == defpx) ? "defaults" : curpx->id),
6041 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006042
Christopher Faulet61cc8522020-04-20 14:54:42 +02006043 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006044 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006045 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006046 if (rs == NULL) {
6047 memprintf(errmsg, "out of memory.\n");
6048 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006049 }
6050 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006051
Christopher Faulete5870d82020-04-15 11:32:03 +02006052 index = 0;
6053 if (!LIST_ISEMPTY(&rs->rules)) {
6054 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6055 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6056 index = chk->index + 1;
6057 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006058
Christopher Faulete5870d82020-04-15 11:32:03 +02006059 if (strcmp(args[cur_arg], "connect") == 0)
6060 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6061 else if (strcmp(args[cur_arg], "send") == 0)
6062 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6063 else if (strcmp(args[cur_arg], "expect") == 0)
6064 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6065 file, line, errmsg);
6066 else if (strcmp(args[cur_arg], "comment") == 0)
6067 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6068 else {
6069 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006070
Christopher Faulete5870d82020-04-15 11:32:03 +02006071 if (!kw) {
6072 action_kw_tcp_check_build_list(&trash);
6073 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6074 " 'send', 'expect'%s%s. but got '%s'",
6075 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6076 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006077 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006078 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6079 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006080
Christopher Faulete5870d82020-04-15 11:32:03 +02006081 if (!chk) {
6082 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6083 goto error;
6084 }
6085 ret = (*errmsg != NULL); /* Handle warning */
6086
6087 chk->index = index;
6088 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6089 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6090 /* Use this ruleset if the proxy already has http-check enabled */
6091 curpx->tcpcheck_rules.list = &rs->rules;
6092 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6093 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6094 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6095 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006096 goto error;
6097 }
6098 }
6099 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006100 /* mark this ruleset as unused for now */
6101 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6102 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006103 }
6104
Christopher Faulete5870d82020-04-15 11:32:03 +02006105 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006106 return ret;
6107
6108 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006109 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006110 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006111 return -1;
6112}
6113
Christopher Faulete9111b62020-04-09 18:12:08 +02006114/* Parses the "external-check" proxy keyword */
6115static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6116 struct proxy *defpx, const char *file, int line,
6117 char **errmsg)
6118{
6119 int cur_arg, ret = 0;
6120
6121 cur_arg = 1;
6122 if (!*(args[cur_arg])) {
6123 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6124 goto error;
6125 }
6126
6127 if (strcmp(args[cur_arg], "command") == 0) {
6128 if (too_many_args(2, args, errmsg, NULL))
6129 goto error;
6130 if (!*(args[cur_arg+1])) {
6131 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6132 goto error;
6133 }
6134 free(curpx->check_command);
6135 curpx->check_command = strdup(args[cur_arg+1]);
6136 }
6137 else if (strcmp(args[cur_arg], "path") == 0) {
6138 if (too_many_args(2, args, errmsg, NULL))
6139 goto error;
6140 if (!*(args[cur_arg+1])) {
6141 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6142 goto error;
6143 }
6144 free(curpx->check_path);
6145 curpx->check_path = strdup(args[cur_arg+1]);
6146 }
6147 else {
6148 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6149 args[0], args[1]);
6150 goto error;
6151 }
6152
6153 ret = (*errmsg != NULL); /* Handle warning */
6154 return ret;
6155
6156error:
6157 return -1;
6158}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006159
Christopher Faulet430e4802020-04-09 15:28:16 +02006160/* Parses the "option tcp-check" proxy keyword */
6161int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6162 const char *file, int line)
6163{
Christopher Faulet404f9192020-04-09 23:13:54 +02006164 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006165 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6166 int err_code = 0;
6167
6168 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6169 err_code |= ERR_WARN;
6170
6171 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6172 goto out;
6173
Christopher Faulet404f9192020-04-09 23:13:54 +02006174 curpx->options2 &= ~PR_O2_CHK_ANY;
6175 curpx->options2 |= PR_O2_TCPCHK_CHK;
6176
Christopher Fauletd7e63962020-04-17 20:15:59 +02006177 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006178 /* If a tcp-check rulesset is already set, do nothing */
6179 if (rules->list)
6180 goto out;
6181
6182 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6183 * get it.
6184 */
6185 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6186 goto curpx_ruleset;
6187
6188 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6189 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006190 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006191 if (rs)
6192 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006193 }
6194
Christopher Faulet404f9192020-04-09 23:13:54 +02006195 curpx_ruleset:
6196 /* Deduce the ruleset name from the proxy info */
6197 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6198 ((curpx == defpx) ? "defaults" : curpx->id),
6199 curpx->conf.file, curpx->conf.line);
6200
Christopher Faulet61cc8522020-04-20 14:54:42 +02006201 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006202 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006203 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006204 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006205 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6206 goto error;
6207 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006208 }
6209
Christopher Faulet404f9192020-04-09 23:13:54 +02006210 ruleset_found:
6211 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006212 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006213 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006214
6215 out:
6216 return err_code;
6217
6218 error:
6219 err_code |= ERR_ALERT | ERR_FATAL;
6220 goto out;
6221}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006222
6223/* Parses the "option redis-check" proxy keyword */
6224int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6225 const char *file, int line)
6226{
6227 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6228 static char *redis_res = "+PONG\r\n";
6229
6230 struct tcpcheck_ruleset *rs = NULL;
6231 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6232 struct tcpcheck_rule *chk;
6233 char *errmsg = NULL;
6234 int err_code = 0;
6235
6236 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6237 err_code |= ERR_WARN;
6238
6239 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6240 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006241
6242 curpx->options2 &= ~PR_O2_CHK_ANY;
6243 curpx->options2 |= PR_O2_TCPCHK_CHK;
6244
6245 free_tcpcheck_vars(&rules->preset_vars);
6246 rules->list = NULL;
6247 rules->flags = 0;
6248
Christopher Faulet61cc8522020-04-20 14:54:42 +02006249 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006250 if (rs)
6251 goto ruleset_found;
6252
Christopher Faulet61cc8522020-04-20 14:54:42 +02006253 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006254 if (rs == NULL) {
6255 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6256 goto error;
6257 }
6258
6259 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6260 1, curpx, &rs->rules, file, line, &errmsg);
6261 if (!chk) {
6262 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6263 goto error;
6264 }
6265 chk->index = 0;
6266 LIST_ADDQ(&rs->rules, &chk->list);
6267
6268 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6269 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006270 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006271 "on-success", "Redis server is ok",
6272 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006273 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006274 if (!chk) {
6275 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6276 goto error;
6277 }
6278 chk->index = 1;
6279 LIST_ADDQ(&rs->rules, &chk->list);
6280
Christopher Faulet33f05df2020-04-01 11:08:50 +02006281 ruleset_found:
6282 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006283 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006284
6285 out:
6286 free(errmsg);
6287 return err_code;
6288
6289 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006290 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006291 err_code |= ERR_ALERT | ERR_FATAL;
6292 goto out;
6293}
6294
Christopher Faulet811f78c2020-04-01 11:10:27 +02006295
6296/* Parses the "option ssl-hello-chk" proxy keyword */
6297int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6298 const char *file, int line)
6299{
6300 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6301 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6302 *
6303 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6304 */
6305 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006306 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006307 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6308 "0079" /* ContentLength : 0x79 bytes after this one */
6309 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6310 "000075" /* HandshakeLength : 0x75 bytes after this one */
6311 "0300" /* Hello Version : 0x0300 = v3 */
6312 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6313 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6314 "00" /* Session ID length : empty (no session ID) */
6315 "004E" /* Cipher Suite Length : 78 bytes after this one */
6316 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6317 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6318 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6319 "000D" "000E" "000F" "0010" /* various bit lengths, */
6320 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6321 "0015" "0016" "0017" "0018"
6322 "0019" "001A" "001B" "002F"
6323 "0030" "0031" "0032" "0033"
6324 "0034" "0035" "0036" "0037"
6325 "0038" "0039" "003A"
6326 "01" /* Compression Length : 0x01 = 1 byte for types */
6327 "00" /* Compression Type : 0x00 = NULL compression */
6328 };
6329
6330 struct tcpcheck_ruleset *rs = NULL;
6331 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6332 struct tcpcheck_rule *chk;
6333 char *errmsg = NULL;
6334 int err_code = 0;
6335
6336 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6337 err_code |= ERR_WARN;
6338
6339 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6340 goto out;
6341
Christopher Faulet811f78c2020-04-01 11:10:27 +02006342 curpx->options2 &= ~PR_O2_CHK_ANY;
6343 curpx->options2 |= PR_O2_TCPCHK_CHK;
6344
6345 free_tcpcheck_vars(&rules->preset_vars);
6346 rules->list = NULL;
6347 rules->flags = 0;
6348
Christopher Faulet61cc8522020-04-20 14:54:42 +02006349 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006350 if (rs)
6351 goto ruleset_found;
6352
Christopher Faulet61cc8522020-04-20 14:54:42 +02006353 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006354 if (rs == NULL) {
6355 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6356 goto error;
6357 }
6358
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006359 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006360 1, curpx, &rs->rules, file, line, &errmsg);
6361 if (!chk) {
6362 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6363 goto error;
6364 }
6365 chk->index = 0;
6366 LIST_ADDQ(&rs->rules, &chk->list);
6367
6368 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006369 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006370 "error-status", "L6RSP", "tout-status", "L6TOUT",
6371 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006372 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006373 if (!chk) {
6374 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6375 goto error;
6376 }
6377 chk->index = 1;
6378 LIST_ADDQ(&rs->rules, &chk->list);
6379
Christopher Faulet811f78c2020-04-01 11:10:27 +02006380 ruleset_found:
6381 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006382 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006383
6384 out:
6385 free(errmsg);
6386 return err_code;
6387
6388 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006389 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006390 err_code |= ERR_ALERT | ERR_FATAL;
6391 goto out;
6392}
6393
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006394/* Parses the "option smtpchk" proxy keyword */
6395int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6396 const char *file, int line)
6397{
6398 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6399
6400 struct tcpcheck_ruleset *rs = NULL;
6401 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6402 struct tcpcheck_rule *chk;
6403 struct tcpcheck_var *var = NULL;
6404 char *cmd = NULL, *errmsg = NULL;
6405 int err_code = 0;
6406
6407 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6408 err_code |= ERR_WARN;
6409
6410 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6411 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006412
6413 curpx->options2 &= ~PR_O2_CHK_ANY;
6414 curpx->options2 |= PR_O2_TCPCHK_CHK;
6415
6416 free_tcpcheck_vars(&rules->preset_vars);
6417 rules->list = NULL;
6418 rules->flags = 0;
6419
6420 cur_arg += 2;
6421 if (*args[cur_arg] && *args[cur_arg+1] &&
6422 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6423 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6424 if (cmd)
6425 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6426 }
6427 else {
6428 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6429 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6430 cmd = strdup("HELO localhost");
6431 }
6432
Christopher Fauletb61caf42020-04-21 10:57:42 +02006433 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006434 if (cmd == NULL || var == NULL) {
6435 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6436 goto error;
6437 }
6438 var->data.type = SMP_T_STR;
6439 var->data.u.str.area = cmd;
6440 var->data.u.str.data = strlen(cmd);
6441 LIST_INIT(&var->list);
6442 LIST_ADDQ(&rules->preset_vars, &var->list);
6443 cmd = NULL;
6444 var = NULL;
6445
Christopher Faulet61cc8522020-04-20 14:54:42 +02006446 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006447 if (rs)
6448 goto ruleset_found;
6449
Christopher Faulet61cc8522020-04-20 14:54:42 +02006450 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006451 if (rs == NULL) {
6452 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6453 goto error;
6454 }
6455
6456 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6457 1, curpx, &rs->rules, file, line, &errmsg);
6458 if (!chk) {
6459 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6460 goto error;
6461 }
6462 chk->index = 0;
6463 LIST_ADDQ(&rs->rules, &chk->list);
6464
6465 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6466 "min-recv", "4",
6467 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006468 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006469 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006470 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006471 if (!chk) {
6472 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6473 goto error;
6474 }
6475 chk->index = 1;
6476 LIST_ADDQ(&rs->rules, &chk->list);
6477
6478 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6479 "min-recv", "4",
6480 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006481 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6482 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006483 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006484 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006485 if (!chk) {
6486 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6487 goto error;
6488 }
6489 chk->index = 2;
6490 LIST_ADDQ(&rs->rules, &chk->list);
6491
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006492 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006493 1, curpx, &rs->rules, file, line, &errmsg);
6494 if (!chk) {
6495 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6496 goto error;
6497 }
6498 chk->index = 3;
6499 LIST_ADDQ(&rs->rules, &chk->list);
6500
6501 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6502 "min-recv", "4",
6503 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006504 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6505 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6506 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006507 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006508 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006509 if (!chk) {
6510 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6511 goto error;
6512 }
6513 chk->index = 4;
6514 LIST_ADDQ(&rs->rules, &chk->list);
6515
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006516 ruleset_found:
6517 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006518 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006519
6520 out:
6521 free(errmsg);
6522 return err_code;
6523
6524 error:
6525 free(cmd);
6526 free(var);
6527 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006528 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006529 err_code |= ERR_ALERT | ERR_FATAL;
6530 goto out;
6531}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006532
Christopher Fauletce355072020-04-02 11:44:39 +02006533/* Parses the "option pgsql-check" proxy keyword */
6534int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6535 const char *file, int line)
6536{
6537 static char pgsql_req[] = {
6538 "%[var(check.plen),htonl,hex]" /* The packet length*/
6539 "00030000" /* the version 3.0 */
6540 "7573657200" /* "user" key */
6541 "%[var(check.username),hex]00" /* the username */
6542 "00"
6543 };
6544
6545 struct tcpcheck_ruleset *rs = NULL;
6546 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6547 struct tcpcheck_rule *chk;
6548 struct tcpcheck_var *var = NULL;
6549 char *user = NULL, *errmsg = NULL;
6550 size_t packetlen = 0;
6551 int err_code = 0;
6552
6553 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6554 err_code |= ERR_WARN;
6555
6556 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6557 goto out;
6558
Christopher Fauletce355072020-04-02 11:44:39 +02006559 curpx->options2 &= ~PR_O2_CHK_ANY;
6560 curpx->options2 |= PR_O2_TCPCHK_CHK;
6561
6562 free_tcpcheck_vars(&rules->preset_vars);
6563 rules->list = NULL;
6564 rules->flags = 0;
6565
6566 cur_arg += 2;
6567 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6568 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6569 file, line, args[0], args[1]);
6570 goto error;
6571 }
6572 if (strcmp(args[cur_arg], "user") == 0) {
6573 packetlen = 15 + strlen(args[cur_arg+1]);
6574 user = strdup(args[cur_arg+1]);
6575
Christopher Fauletb61caf42020-04-21 10:57:42 +02006576 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006577 if (user == NULL || var == NULL) {
6578 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6579 goto error;
6580 }
6581 var->data.type = SMP_T_STR;
6582 var->data.u.str.area = user;
6583 var->data.u.str.data = strlen(user);
6584 LIST_INIT(&var->list);
6585 LIST_ADDQ(&rules->preset_vars, &var->list);
6586 user = NULL;
6587 var = NULL;
6588
Christopher Fauletb61caf42020-04-21 10:57:42 +02006589 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006590 if (var == NULL) {
6591 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6592 goto error;
6593 }
6594 var->data.type = SMP_T_SINT;
6595 var->data.u.sint = packetlen;
6596 LIST_INIT(&var->list);
6597 LIST_ADDQ(&rules->preset_vars, &var->list);
6598 var = NULL;
6599 }
6600 else {
6601 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6602 file, line, args[0], args[1]);
6603 goto error;
6604 }
6605
Christopher Faulet61cc8522020-04-20 14:54:42 +02006606 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006607 if (rs)
6608 goto ruleset_found;
6609
Christopher Faulet61cc8522020-04-20 14:54:42 +02006610 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006611 if (rs == NULL) {
6612 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6613 goto error;
6614 }
6615
6616 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6617 1, curpx, &rs->rules, file, line, &errmsg);
6618 if (!chk) {
6619 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6620 goto error;
6621 }
6622 chk->index = 0;
6623 LIST_ADDQ(&rs->rules, &chk->list);
6624
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006625 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006626 1, curpx, &rs->rules, file, line, &errmsg);
6627 if (!chk) {
6628 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6629 goto error;
6630 }
6631 chk->index = 1;
6632 LIST_ADDQ(&rs->rules, &chk->list);
6633
6634 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6635 "min-recv", "5",
6636 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006637 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006638 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006639 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006640 if (!chk) {
6641 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6642 goto error;
6643 }
6644 chk->index = 2;
6645 LIST_ADDQ(&rs->rules, &chk->list);
6646
Christopher Fauletb841c742020-04-27 18:29:49 +02006647 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 +02006648 "min-recv", "9",
6649 "error-status", "L7STS",
6650 "on-success", "PostgreSQL server is ok",
6651 "on-error", "PostgreSQL unknown error",
6652 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006653 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006654 if (!chk) {
6655 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6656 goto error;
6657 }
6658 chk->index = 3;
6659 LIST_ADDQ(&rs->rules, &chk->list);
6660
Christopher Fauletce355072020-04-02 11:44:39 +02006661 ruleset_found:
6662 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006663 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006664
6665 out:
6666 free(errmsg);
6667 return err_code;
6668
6669 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006670 free(user);
6671 free(var);
6672 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006673 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006674 err_code |= ERR_ALERT | ERR_FATAL;
6675 goto out;
6676}
6677
6678
6679/* Parses the "option mysql-check" proxy keyword */
6680int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6681 const char *file, int line)
6682{
6683 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6684 * const char mysql40_client_auth_pkt[] = {
6685 * "\x0e\x00\x00" // packet length
6686 * "\x01" // packet number
6687 * "\x00\x00" // client capabilities
6688 * "\x00\x00\x01" // max packet
6689 * "haproxy\x00" // username (null terminated string)
6690 * "\x00" // filler (always 0x00)
6691 * "\x01\x00\x00" // packet length
6692 * "\x00" // packet number
6693 * "\x01" // COM_QUIT command
6694 * };
6695 */
6696 static char mysql40_rsname[] = "*mysql40-check";
6697 static char mysql40_req[] = {
6698 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6699 "0080" /* client capabilities */
6700 "000001" /* max packet */
6701 "%[var(check.username),hex]00" /* the username */
6702 "00" /* filler (always 0x00) */
6703 "010000" /* packet length*/
6704 "00" /* sequence ID */
6705 "01" /* COM_QUIT command */
6706 };
6707
6708 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6709 * const char mysql41_client_auth_pkt[] = {
6710 * "\x0e\x00\x00\" // packet length
6711 * "\x01" // packet number
6712 * "\x00\x00\x00\x00" // client capabilities
6713 * "\x00\x00\x00\x01" // max packet
6714 * "\x21" // character set (UTF-8)
6715 * char[23] // All zeroes
6716 * "haproxy\x00" // username (null terminated string)
6717 * "\x00" // filler (always 0x00)
6718 * "\x01\x00\x00" // packet length
6719 * "\x00" // packet number
6720 * "\x01" // COM_QUIT command
6721 * };
6722 */
6723 static char mysql41_rsname[] = "*mysql41-check";
6724 static char mysql41_req[] = {
6725 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6726 "00820000" /* client capabilities */
6727 "00800001" /* max packet */
6728 "21" /* character set (UTF-8) */
6729 "000000000000000000000000" /* 23 bytes, al zeroes */
6730 "0000000000000000000000"
6731 "%[var(check.username),hex]00" /* the username */
6732 "00" /* filler (always 0x00) */
6733 "010000" /* packet length*/
6734 "00" /* sequence ID */
6735 "01" /* COM_QUIT command */
6736 };
6737
6738 struct tcpcheck_ruleset *rs = NULL;
6739 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6740 struct tcpcheck_rule *chk;
6741 struct tcpcheck_var *var = NULL;
6742 char *mysql_rsname = "*mysql-check";
6743 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6744 int index = 0, err_code = 0;
6745
6746 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6747 err_code |= ERR_WARN;
6748
6749 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6750 goto out;
6751
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006752 curpx->options2 &= ~PR_O2_CHK_ANY;
6753 curpx->options2 |= PR_O2_TCPCHK_CHK;
6754
6755 free_tcpcheck_vars(&rules->preset_vars);
6756 rules->list = NULL;
6757 rules->flags = 0;
6758
6759 cur_arg += 2;
6760 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006761 int packetlen, userlen;
6762
6763 if (strcmp(args[cur_arg], "user") != 0) {
6764 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6765 file, line, args[0], args[1], args[cur_arg]);
6766 goto error;
6767 }
6768
6769 if (*(args[cur_arg+1]) == 0) {
6770 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6771 file, line, args[0], args[1], args[cur_arg]);
6772 goto error;
6773 }
6774
6775 hdr = calloc(4, sizeof(*hdr));
6776 user = strdup(args[cur_arg+1]);
6777 userlen = strlen(args[cur_arg+1]);
6778
6779 if (hdr == NULL || user == NULL) {
6780 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6781 goto error;
6782 }
6783
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006784 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006785 packetlen = userlen + 7 + 27;
6786 mysql_req = mysql41_req;
6787 mysql_rsname = mysql41_rsname;
6788 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006789 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006790 packetlen = userlen + 7;
6791 mysql_req = mysql40_req;
6792 mysql_rsname = mysql40_rsname;
6793 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006794 else {
6795 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
6796 file, line, args[cur_arg], args[cur_arg+2]);
6797 goto error;
6798 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006799
6800 hdr[0] = (unsigned char)(packetlen & 0xff);
6801 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6802 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6803 hdr[3] = 1;
6804
Christopher Fauletb61caf42020-04-21 10:57:42 +02006805 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006806 if (var == NULL) {
6807 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6808 goto error;
6809 }
6810 var->data.type = SMP_T_STR;
6811 var->data.u.str.area = hdr;
6812 var->data.u.str.data = 4;
6813 LIST_INIT(&var->list);
6814 LIST_ADDQ(&rules->preset_vars, &var->list);
6815 hdr = NULL;
6816 var = NULL;
6817
Christopher Fauletb61caf42020-04-21 10:57:42 +02006818 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006819 if (var == NULL) {
6820 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6821 goto error;
6822 }
6823 var->data.type = SMP_T_STR;
6824 var->data.u.str.area = user;
6825 var->data.u.str.data = strlen(user);
6826 LIST_INIT(&var->list);
6827 LIST_ADDQ(&rules->preset_vars, &var->list);
6828 user = NULL;
6829 var = NULL;
6830 }
6831
Christopher Faulet61cc8522020-04-20 14:54:42 +02006832 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006833 if (rs)
6834 goto ruleset_found;
6835
Christopher Faulet61cc8522020-04-20 14:54:42 +02006836 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006837 if (rs == NULL) {
6838 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6839 goto error;
6840 }
6841
6842 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6843 1, curpx, &rs->rules, file, line, &errmsg);
6844 if (!chk) {
6845 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6846 goto error;
6847 }
6848 chk->index = index++;
6849 LIST_ADDQ(&rs->rules, &chk->list);
6850
6851 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006852 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006853 1, curpx, &rs->rules, file, line, &errmsg);
6854 if (!chk) {
6855 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6856 goto error;
6857 }
6858 chk->index = index++;
6859 LIST_ADDQ(&rs->rules, &chk->list);
6860 }
6861
6862 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006863 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006864 if (!chk) {
6865 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6866 goto error;
6867 }
6868 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6869 chk->index = index++;
6870 LIST_ADDQ(&rs->rules, &chk->list);
6871
6872 if (mysql_req) {
6873 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006874 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006875 if (!chk) {
6876 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6877 goto error;
6878 }
6879 chk->expect.custom = tcpcheck_mysql_expect_ok;
6880 chk->index = index++;
6881 LIST_ADDQ(&rs->rules, &chk->list);
6882 }
6883
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006884 ruleset_found:
6885 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006886 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006887
6888 out:
6889 free(errmsg);
6890 return err_code;
6891
6892 error:
6893 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006894 free(user);
6895 free(var);
6896 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006897 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006898 err_code |= ERR_ALERT | ERR_FATAL;
6899 goto out;
6900}
6901
Christopher Faulet1997eca2020-04-03 23:13:50 +02006902int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6903 const char *file, int line)
6904{
6905 static char *ldap_req = "300C020101600702010304008000";
6906
6907 struct tcpcheck_ruleset *rs = NULL;
6908 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6909 struct tcpcheck_rule *chk;
6910 char *errmsg = NULL;
6911 int err_code = 0;
6912
6913 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6914 err_code |= ERR_WARN;
6915
6916 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6917 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006918
6919 curpx->options2 &= ~PR_O2_CHK_ANY;
6920 curpx->options2 |= PR_O2_TCPCHK_CHK;
6921
6922 free_tcpcheck_vars(&rules->preset_vars);
6923 rules->list = NULL;
6924 rules->flags = 0;
6925
Christopher Faulet61cc8522020-04-20 14:54:42 +02006926 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006927 if (rs)
6928 goto ruleset_found;
6929
Christopher Faulet61cc8522020-04-20 14:54:42 +02006930 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006931 if (rs == NULL) {
6932 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6933 goto error;
6934 }
6935
6936 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6937 1, curpx, &rs->rules, file, line, &errmsg);
6938 if (!chk) {
6939 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6940 goto error;
6941 }
6942 chk->index = 0;
6943 LIST_ADDQ(&rs->rules, &chk->list);
6944
6945 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6946 "min-recv", "14",
6947 "on-error", "Not LDAPv3 protocol",
6948 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006949 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006950 if (!chk) {
6951 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6952 goto error;
6953 }
6954 chk->index = 1;
6955 LIST_ADDQ(&rs->rules, &chk->list);
6956
6957 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006958 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006959 if (!chk) {
6960 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6961 goto error;
6962 }
6963 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6964 chk->index = 2;
6965 LIST_ADDQ(&rs->rules, &chk->list);
6966
Christopher Faulet1997eca2020-04-03 23:13:50 +02006967 ruleset_found:
6968 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006969 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006970
6971 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006972 free(errmsg);
6973 return err_code;
6974
6975 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006976 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006977 err_code |= ERR_ALERT | ERR_FATAL;
6978 goto out;
6979}
6980
6981int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6982 const char *file, int line)
6983{
6984 struct tcpcheck_ruleset *rs = NULL;
6985 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6986 struct tcpcheck_rule *chk;
6987 char *spop_req = NULL;
6988 char *errmsg = NULL;
6989 int spop_len = 0, err_code = 0;
6990
6991 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6992 err_code |= ERR_WARN;
6993
6994 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6995 goto out;
6996
Christopher Faulet267b01b2020-04-04 10:27:09 +02006997 curpx->options2 &= ~PR_O2_CHK_ANY;
6998 curpx->options2 |= PR_O2_TCPCHK_CHK;
6999
7000 free_tcpcheck_vars(&rules->preset_vars);
7001 rules->list = NULL;
7002 rules->flags = 0;
7003
7004
Christopher Faulet61cc8522020-04-20 14:54:42 +02007005 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007006 if (rs)
7007 goto ruleset_found;
7008
Christopher Faulet61cc8522020-04-20 14:54:42 +02007009 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007010 if (rs == NULL) {
7011 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7012 goto error;
7013 }
7014
7015 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7016 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7017 goto error;
7018 }
7019 chunk_reset(&trash);
7020 dump_binary(&trash, spop_req, spop_len);
7021 trash.area[trash.data] = '\0';
7022
7023 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7024 1, curpx, &rs->rules, file, line, &errmsg);
7025 if (!chk) {
7026 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7027 goto error;
7028 }
7029 chk->index = 0;
7030 LIST_ADDQ(&rs->rules, &chk->list);
7031
7032 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007033 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007034 if (!chk) {
7035 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7036 goto error;
7037 }
7038 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7039 chk->index = 1;
7040 LIST_ADDQ(&rs->rules, &chk->list);
7041
Christopher Faulet267b01b2020-04-04 10:27:09 +02007042 ruleset_found:
7043 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007044 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007045
7046 out:
7047 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007048 free(errmsg);
7049 return err_code;
7050
7051 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007052 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007053 err_code |= ERR_ALERT | ERR_FATAL;
7054 goto out;
7055}
Christopher Fauletce355072020-04-02 11:44:39 +02007056
Christopher Faulete5870d82020-04-15 11:32:03 +02007057
7058struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7059{
7060 struct tcpcheck_rule *chk = NULL;
7061 struct tcpcheck_http_hdr *hdr = NULL;
7062 char *meth = NULL, *uri = NULL, *vsn = NULL;
7063 char *hdrs, *body;
7064
7065 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7066 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7067 if (hdrs == body)
7068 hdrs = NULL;
7069 if (hdrs) {
7070 *hdrs = '\0';
7071 hdrs +=2;
7072 }
7073 if (body) {
7074 *body = '\0';
7075 body += 4;
7076 }
7077 if (hdrs || body) {
7078 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7079 " Please, consider to use 'http-check send' directive instead.");
7080 }
7081
7082 chk = calloc(1, sizeof(*chk));
7083 if (!chk) {
7084 memprintf(errmsg, "out of memory");
7085 goto error;
7086 }
7087 chk->action = TCPCHK_ACT_SEND;
7088 chk->send.type = TCPCHK_SEND_HTTP;
7089 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7090 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7091 LIST_INIT(&chk->send.http.hdrs);
7092
7093 /* Copy the method, uri and version */
7094 if (*args[cur_arg]) {
7095 if (!*args[cur_arg+1])
7096 uri = args[cur_arg];
7097 else
7098 meth = args[cur_arg];
7099 }
7100 if (*args[cur_arg+1])
7101 uri = args[cur_arg+1];
7102 if (*args[cur_arg+2])
7103 vsn = args[cur_arg+2];
7104
7105 if (meth) {
7106 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7107 chk->send.http.meth.str.area = strdup(meth);
7108 chk->send.http.meth.str.data = strlen(meth);
7109 if (!chk->send.http.meth.str.area) {
7110 memprintf(errmsg, "out of memory");
7111 goto error;
7112 }
7113 }
7114 if (uri) {
7115 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007116 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007117 memprintf(errmsg, "out of memory");
7118 goto error;
7119 }
7120 }
7121 if (vsn) {
7122 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007123 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007124 memprintf(errmsg, "out of memory");
7125 goto error;
7126 }
7127 }
7128
7129 /* Copy the header */
7130 if (hdrs) {
7131 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7132 struct h1m h1m;
7133 int i, ret;
7134
7135 /* Build and parse the request */
7136 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7137
7138 h1m.flags = H1_MF_HDRS_ONLY;
7139 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7140 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7141 &h1m, NULL);
7142 if (ret <= 0) {
7143 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7144 goto error;
7145 }
7146
Christopher Fauletb61caf42020-04-21 10:57:42 +02007147 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007148 hdr = calloc(1, sizeof(*hdr));
7149 if (!hdr) {
7150 memprintf(errmsg, "out of memory");
7151 goto error;
7152 }
7153 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007154 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007155 if (!hdr->name.ptr) {
7156 memprintf(errmsg, "out of memory");
7157 goto error;
7158 }
7159
Christopher Fauletb61caf42020-04-21 10:57:42 +02007160 ist0(tmp_hdrs[i].v);
7161 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 +02007162 goto error;
7163 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7164 }
7165 }
7166
7167 /* Copy the body */
7168 if (body) {
7169 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007170 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007171 memprintf(errmsg, "out of memory");
7172 goto error;
7173 }
7174 }
7175
7176 return chk;
7177
7178 error:
7179 free_tcpcheck_http_hdr(hdr);
7180 free_tcpcheck(chk, 0);
7181 return NULL;
7182}
7183
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007184int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7185 const char *file, int line)
7186{
Christopher Faulete5870d82020-04-15 11:32:03 +02007187 struct tcpcheck_ruleset *rs = NULL;
7188 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7189 struct tcpcheck_rule *chk;
7190 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007191 int err_code = 0;
7192
7193 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7194 err_code |= ERR_WARN;
7195
7196 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7197 goto out;
7198
Christopher Faulete5870d82020-04-15 11:32:03 +02007199 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7200 if (!chk) {
7201 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7202 goto error;
7203 }
7204 if (errmsg) {
7205 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7206 err_code |= ERR_WARN;
7207 free(errmsg);
7208 errmsg = NULL;
7209 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007210
Christopher Faulete5870d82020-04-15 11:32:03 +02007211 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007212 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007213 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007214
Christopher Faulete5870d82020-04-15 11:32:03 +02007215 free_tcpcheck_vars(&rules->preset_vars);
7216 rules->list = NULL;
7217 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007218
Christopher Faulete5870d82020-04-15 11:32:03 +02007219 /* Deduce the ruleset name from the proxy info */
7220 chunk_printf(&trash, "*http-check-%s_%s-%d",
7221 ((curpx == defpx) ? "defaults" : curpx->id),
7222 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007223
Christopher Faulet61cc8522020-04-20 14:54:42 +02007224 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007225 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007226 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007227 if (rs == NULL) {
7228 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7229 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007230 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007231 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007232
Christopher Faulete5870d82020-04-15 11:32:03 +02007233 rules->list = &rs->rules;
7234 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7235 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7236 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7237 rules->list = NULL;
7238 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007239 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007240
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007241 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007242 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007243 return err_code;
7244
7245 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007246 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007247 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007248 err_code |= ERR_ALERT | ERR_FATAL;
7249 goto out;
7250}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007251
Christopher Faulet6f557912020-04-09 15:58:50 +02007252int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7253 const char *file, int line)
7254{
7255 int err_code = 0;
7256
Christopher Faulet6f557912020-04-09 15:58:50 +02007257 curpx->options2 &= ~PR_O2_CHK_ANY;
7258 curpx->options2 |= PR_O2_EXT_CHK;
7259 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7260 goto out;
7261
7262 out:
7263 return err_code;
7264}
7265
Christopher Fauletce8111e2020-04-06 15:04:11 +02007266/* Parse the "addr" server keyword */
7267static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7268 char **errmsg)
7269{
7270 struct sockaddr_storage *sk;
7271 struct protocol *proto;
7272 int port1, port2, err_code = 0;
7273
7274
7275 if (!*args[*cur_arg+1]) {
7276 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7277 goto error;
7278 }
7279
7280 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7281 if (!sk) {
7282 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7283 goto error;
7284 }
7285
7286 proto = protocol_by_family(sk->ss_family);
7287 if (!proto || !proto->connect) {
7288 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7289 args[*cur_arg], args[*cur_arg+1]);
7290 goto error;
7291 }
7292
7293 if (port1 != port2) {
7294 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7295 args[*cur_arg], args[*cur_arg+1]);
7296 goto error;
7297 }
7298
7299 srv->check.addr = srv->agent.addr = *sk;
7300 srv->flags |= SRV_F_CHECKADDR;
7301 srv->flags |= SRV_F_AGENTADDR;
7302
7303 out:
7304 return err_code;
7305
7306 error:
7307 err_code |= ERR_ALERT | ERR_FATAL;
7308 goto out;
7309}
7310
7311
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007312/* Parse the "agent-addr" server keyword */
7313static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7314 char **errmsg)
7315{
7316 int err_code = 0;
7317
7318 if (!*(args[*cur_arg+1])) {
7319 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7320 goto error;
7321 }
7322 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7323 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7324 goto error;
7325 }
7326
7327 out:
7328 return err_code;
7329
7330 error:
7331 err_code |= ERR_ALERT | ERR_FATAL;
7332 goto out;
7333}
7334
7335/* Parse the "agent-check" server keyword */
7336static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7337 char **errmsg)
7338{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007339 struct tcpcheck_ruleset *rs = NULL;
7340 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7341 struct tcpcheck_rule *chk;
7342 int err_code = 0;
7343
7344 if (srv->do_agent)
7345 goto out;
7346
7347 if (!rules) {
7348 rules = calloc(1, sizeof(*rules));
7349 if (!rules) {
7350 memprintf(errmsg, "out of memory.");
7351 goto error;
7352 }
7353 LIST_INIT(&rules->preset_vars);
7354 srv->agent.tcpcheck_rules = rules;
7355 }
7356 rules->list = NULL;
7357 rules->flags = 0;
7358
Christopher Faulet61cc8522020-04-20 14:54:42 +02007359 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007360 if (rs)
7361 goto ruleset_found;
7362
Christopher Faulet61cc8522020-04-20 14:54:42 +02007363 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007364 if (rs == NULL) {
7365 memprintf(errmsg, "out of memory.");
7366 goto error;
7367 }
7368
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007369 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007370 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7371 if (!chk) {
7372 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7373 goto error;
7374 }
7375 chk->index = 0;
7376 LIST_ADDQ(&rs->rules, &chk->list);
7377
7378 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007379 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7380 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007381 if (!chk) {
7382 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7383 goto error;
7384 }
7385 chk->expect.custom = tcpcheck_agent_expect_reply;
7386 chk->index = 1;
7387 LIST_ADDQ(&rs->rules, &chk->list);
7388
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007389 ruleset_found:
7390 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007391 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007392 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007393
7394 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007395 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007396
7397 error:
7398 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007399 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007400 err_code |= ERR_ALERT | ERR_FATAL;
7401 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007402}
7403
7404/* Parse the "agent-inter" server keyword */
7405static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7406 char **errmsg)
7407{
7408 const char *err = NULL;
7409 unsigned int delay;
7410 int err_code = 0;
7411
7412 if (!*(args[*cur_arg+1])) {
7413 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7414 goto error;
7415 }
7416
7417 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7418 if (err == PARSE_TIME_OVER) {
7419 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7420 args[*cur_arg+1], args[*cur_arg], srv->id);
7421 goto error;
7422 }
7423 else if (err == PARSE_TIME_UNDER) {
7424 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7425 args[*cur_arg+1], args[*cur_arg], srv->id);
7426 goto error;
7427 }
7428 else if (err) {
7429 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7430 *err, srv->id);
7431 goto error;
7432 }
7433 if (delay <= 0) {
7434 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7435 delay, args[*cur_arg], srv->id);
7436 goto error;
7437 }
7438 srv->agent.inter = delay;
7439
7440 out:
7441 return err_code;
7442
7443 error:
7444 err_code |= ERR_ALERT | ERR_FATAL;
7445 goto out;
7446}
7447
7448/* Parse the "agent-port" server keyword */
7449static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7450 char **errmsg)
7451{
7452 int err_code = 0;
7453
7454 if (!*(args[*cur_arg+1])) {
7455 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7456 goto error;
7457 }
7458
7459 global.maxsock++;
7460 srv->agent.port = atol(args[*cur_arg+1]);
7461
7462 out:
7463 return err_code;
7464
7465 error:
7466 err_code |= ERR_ALERT | ERR_FATAL;
7467 goto out;
7468}
7469
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007470int set_srv_agent_send(struct server *srv, const char *send)
7471{
7472 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7473 struct tcpcheck_var *var = NULL;
7474 char *str;
7475
7476 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007477 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007478 if (str == NULL || var == NULL)
7479 goto error;
7480
7481 free_tcpcheck_vars(&rules->preset_vars);
7482
7483 var->data.type = SMP_T_STR;
7484 var->data.u.str.area = str;
7485 var->data.u.str.data = strlen(str);
7486 LIST_INIT(&var->list);
7487 LIST_ADDQ(&rules->preset_vars, &var->list);
7488
7489 return 1;
7490
7491 error:
7492 free(str);
7493 free(var);
7494 return 0;
7495}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007496
7497/* Parse the "agent-send" server keyword */
7498static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7499 char **errmsg)
7500{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007501 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007502 int err_code = 0;
7503
7504 if (!*(args[*cur_arg+1])) {
7505 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7506 goto error;
7507 }
7508
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007509 if (!rules) {
7510 rules = calloc(1, sizeof(*rules));
7511 if (!rules) {
7512 memprintf(errmsg, "out of memory.");
7513 goto error;
7514 }
7515 LIST_INIT(&rules->preset_vars);
7516 srv->agent.tcpcheck_rules = rules;
7517 }
7518
7519 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007520 memprintf(errmsg, "out of memory.");
7521 goto error;
7522 }
7523
7524 out:
7525 return err_code;
7526
7527 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007528 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007529 err_code |= ERR_ALERT | ERR_FATAL;
7530 goto out;
7531}
7532
7533/* Parse the "no-agent-send" server keyword */
7534static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7535 char **errmsg)
7536{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007537 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007538 return 0;
7539}
7540
Christopher Fauletce8111e2020-04-06 15:04:11 +02007541/* Parse the "check" server keyword */
7542static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7543 char **errmsg)
7544{
7545 srv->do_check = 1;
7546 return 0;
7547}
7548
7549/* Parse the "check-send-proxy" server keyword */
7550static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7551 char **errmsg)
7552{
7553 srv->check.send_proxy = 1;
7554 return 0;
7555}
7556
7557/* Parse the "check-via-socks4" server keyword */
7558static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7559 char **errmsg)
7560{
7561 srv->check.via_socks4 = 1;
7562 return 0;
7563}
7564
7565/* Parse the "no-check" server keyword */
7566static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7567 char **errmsg)
7568{
7569 deinit_srv_check(srv);
7570 return 0;
7571}
7572
7573/* Parse the "no-check-send-proxy" server keyword */
7574static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7575 char **errmsg)
7576{
7577 srv->check.send_proxy = 0;
7578 return 0;
7579}
7580
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007581/* parse the "check-proto" server keyword */
7582static int srv_parse_check_proto(char **args, int *cur_arg,
7583 struct proxy *px, struct server *newsrv, char **err)
7584{
7585 int err_code = 0;
7586
7587 if (!*args[*cur_arg + 1]) {
7588 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7589 goto error;
7590 }
7591 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7592 if (!newsrv->check.mux_proto) {
7593 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7594 goto error;
7595 }
7596
7597 out:
7598 return err_code;
7599
7600 error:
7601 err_code |= ERR_ALERT | ERR_FATAL;
7602 goto out;
7603}
7604
7605
Christopher Fauletce8111e2020-04-06 15:04:11 +02007606/* Parse the "rise" server keyword */
7607static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7608 char **errmsg)
7609{
7610 int err_code = 0;
7611
7612 if (!*args[*cur_arg + 1]) {
7613 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7614 goto error;
7615 }
7616
7617 srv->check.rise = atol(args[*cur_arg+1]);
7618 if (srv->check.rise <= 0) {
7619 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7620 goto error;
7621 }
7622
7623 if (srv->check.health)
7624 srv->check.health = srv->check.rise;
7625
7626 out:
7627 return err_code;
7628
7629 error:
7630 deinit_srv_agent_check(srv);
7631 err_code |= ERR_ALERT | ERR_FATAL;
7632 goto out;
7633 return 0;
7634}
7635
7636/* Parse the "fall" server keyword */
7637static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7638 char **errmsg)
7639{
7640 int err_code = 0;
7641
7642 if (!*args[*cur_arg + 1]) {
7643 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7644 goto error;
7645 }
7646
7647 srv->check.fall = atol(args[*cur_arg+1]);
7648 if (srv->check.fall <= 0) {
7649 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7650 goto error;
7651 }
7652
7653 out:
7654 return err_code;
7655
7656 error:
7657 deinit_srv_agent_check(srv);
7658 err_code |= ERR_ALERT | ERR_FATAL;
7659 goto out;
7660 return 0;
7661}
7662
7663/* Parse the "inter" server keyword */
7664static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7665 char **errmsg)
7666{
7667 const char *err = NULL;
7668 unsigned int delay;
7669 int err_code = 0;
7670
7671 if (!*(args[*cur_arg+1])) {
7672 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7673 goto error;
7674 }
7675
7676 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7677 if (err == PARSE_TIME_OVER) {
7678 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7679 args[*cur_arg+1], args[*cur_arg], srv->id);
7680 goto error;
7681 }
7682 else if (err == PARSE_TIME_UNDER) {
7683 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7684 args[*cur_arg+1], args[*cur_arg], srv->id);
7685 goto error;
7686 }
7687 else if (err) {
7688 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7689 *err, srv->id);
7690 goto error;
7691 }
7692 if (delay <= 0) {
7693 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7694 delay, args[*cur_arg], srv->id);
7695 goto error;
7696 }
7697 srv->check.inter = delay;
7698
7699 out:
7700 return err_code;
7701
7702 error:
7703 err_code |= ERR_ALERT | ERR_FATAL;
7704 goto out;
7705}
7706
7707
7708/* Parse the "fastinter" server keyword */
7709static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7710 char **errmsg)
7711{
7712 const char *err = NULL;
7713 unsigned int delay;
7714 int err_code = 0;
7715
7716 if (!*(args[*cur_arg+1])) {
7717 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7718 goto error;
7719 }
7720
7721 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7722 if (err == PARSE_TIME_OVER) {
7723 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7724 args[*cur_arg+1], args[*cur_arg], srv->id);
7725 goto error;
7726 }
7727 else if (err == PARSE_TIME_UNDER) {
7728 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7729 args[*cur_arg+1], args[*cur_arg], srv->id);
7730 goto error;
7731 }
7732 else if (err) {
7733 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7734 *err, srv->id);
7735 goto error;
7736 }
7737 if (delay <= 0) {
7738 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7739 delay, args[*cur_arg], srv->id);
7740 goto error;
7741 }
7742 srv->check.fastinter = delay;
7743
7744 out:
7745 return err_code;
7746
7747 error:
7748 err_code |= ERR_ALERT | ERR_FATAL;
7749 goto out;
7750}
7751
7752
7753/* Parse the "downinter" server keyword */
7754static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7755 char **errmsg)
7756{
7757 const char *err = NULL;
7758 unsigned int delay;
7759 int err_code = 0;
7760
7761 if (!*(args[*cur_arg+1])) {
7762 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7763 goto error;
7764 }
7765
7766 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7767 if (err == PARSE_TIME_OVER) {
7768 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7769 args[*cur_arg+1], args[*cur_arg], srv->id);
7770 goto error;
7771 }
7772 else if (err == PARSE_TIME_UNDER) {
7773 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7774 args[*cur_arg+1], args[*cur_arg], srv->id);
7775 goto error;
7776 }
7777 else if (err) {
7778 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7779 *err, srv->id);
7780 goto error;
7781 }
7782 if (delay <= 0) {
7783 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7784 delay, args[*cur_arg], srv->id);
7785 goto error;
7786 }
7787 srv->check.downinter = delay;
7788
7789 out:
7790 return err_code;
7791
7792 error:
7793 err_code |= ERR_ALERT | ERR_FATAL;
7794 goto out;
7795}
7796
7797/* Parse the "port" server keyword */
7798static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7799 char **errmsg)
7800{
7801 int err_code = 0;
7802
7803 if (!*(args[*cur_arg+1])) {
7804 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7805 goto error;
7806 }
7807
7808 global.maxsock++;
7809 srv->check.port = atol(args[*cur_arg+1]);
7810 srv->flags |= SRV_F_CHECKPORT;
7811
7812 out:
7813 return err_code;
7814
7815 error:
7816 err_code |= ERR_ALERT | ERR_FATAL;
7817 goto out;
7818}
7819
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007820static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007821 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7822 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7823 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007824 { 0, NULL, NULL },
7825}};
7826
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007827static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007828 { "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 +02007829 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7830 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7831 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7832 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7833 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007834 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007835 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007836 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7837 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007838 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007839 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7840 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7841 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7842 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7843 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7844 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7845 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7846 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007847 { NULL, NULL, 0 },
7848}};
7849
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007850INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007851INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007852
Willy Tarreaubd741542010-03-16 18:46:54 +01007853/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007854 * Local variables:
7855 * c-indent-level: 8
7856 * c-basic-offset: 8
7857 * End:
7858 */