blob: 8b852867fa76cdaa58a6a22d655bd5561fd48843 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Willy Tarreau9b39dc52014-07-08 00:54:10 +020018#include <signal.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090019#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020020#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020021#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020023#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020024#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010025#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040027#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090028#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020030#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031#include <arpa/inet.h>
32
Christopher Fauletfd6c2292020-03-25 18:20:15 +010033#include <common/cfgparse.h>
Willy Tarreauc7e42382012-08-24 19:22:53 +020034#include <common/chunk.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020035#include <common/compat.h>
36#include <common/config.h>
37#include <common/mini-clist.h>
Willy Tarreau83749182007-04-15 20:56:27 +020038#include <common/standard.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020039#include <common/time.h>
Christopher Fauletcfda8472017-10-20 15:40:23 +020040#include <common/hathreads.h>
Christopher Faulete5870d82020-04-15 11:32:03 +020041#include <common/http.h>
42#include <common/h1.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020043#include <common/htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020044
45#include <types/global.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020046#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010047#include <types/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020048
Gaetan Rivet707b52f2020-02-21 18:14:59 +010049#include <proto/action.h>
Christopher Fauletba3c68f2020-04-01 16:27:05 +020050#include <proto/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020051#include <proto/backend.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020052#include <proto/checks.h>
William Lallemand9ed62032016-11-21 17:49:11 +010053#include <proto/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020054#include <proto/fd.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020055#include <proto/http_htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020056#include <proto/log.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020057#include <proto/mux_pt.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020058#include <proto/queue.h>
Willy Tarreauc6f4ce82009-06-10 11:09:37 +020059#include <proto/port_range.h>
Willy Tarreaue8c66af2008-01-13 18:40:14 +010060#include <proto/proto_tcp.h>
Baptiste Assmann69e273f2013-12-11 00:52:19 +010061#include <proto/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010062#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020063#include <proto/server.h>
Willy Tarreau48d6bf22016-06-21 16:27:34 +020064#include <proto/signal.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010065#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020066#include <proto/task.h>
Gaetan Rivet13a50432020-02-21 18:13:44 +010067#include <proto/vars.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020068#include <proto/log.h>
69#include <proto/dns.h>
70#include <proto/proto_udp.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020071#include <proto/ssl_sock.h>
Christopher Fauletb7d30092020-03-30 15:19:03 +020072#include <proto/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020073
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020074static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010075
Christopher Faulet61cc8522020-04-20 14:54:42 +020076static int wake_srv_chk(struct conn_stream *cs);
77struct data_cb check_conn_cb = {
78 .wake = wake_srv_chk,
79 .name = "CHCK",
80};
Christopher Fauletd7e63962020-04-17 20:15:59 +020081
Christopher Fauletd7cee712020-04-21 13:45:00 +020082/* Global tree to share all tcp-checks */
83struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020084
85
Willy Tarreau8ceae722018-11-26 11:58:30 +010086DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
87DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020088
Gaetan Rivet05d692d2020-02-14 17:42:54 +010089/* Dummy frontend used to create all checks sessions. */
90static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020091
Christopher Faulet61cc8522020-04-20 14:54:42 +020092/**************************************************************************/
93/************************ Handle check results ****************************/
94/**************************************************************************/
95struct check_status {
96 short result; /* one of SRV_CHK_* */
97 char *info; /* human readable short info */
98 char *desc; /* long description */
99};
100
101struct analyze_status {
102 char *desc; /* description */
103 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
104};
105
Simon Horman63a4a822012-03-19 07:24:41 +0900106static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100107 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
108 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200109 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200110
Willy Tarreau23964182014-05-20 20:56:30 +0200111 /* Below we have finished checks */
112 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100114
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100115 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100117 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
118 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
119 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200120
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100121 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
122 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
123 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200124
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100125 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
126 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200127
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200128 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200129
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100130 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
131 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
132 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900133
134 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
135 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200136 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200137};
138
Simon Horman63a4a822012-03-19 07:24:41 +0900139static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100140 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
141
142 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
143 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
144
145 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
146 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
147 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
148 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
149
150 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
151 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
152 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
153};
154
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100155/* checks if <err> is a real error for errno or one that can be ignored, and
156 * return 0 for these ones or <err> for real ones.
157 */
158static inline int unclean_errno(int err)
159{
160 if (err == EAGAIN || err == EINPROGRESS ||
161 err == EISCONN || err == EALREADY)
162 return 0;
163 return err;
164}
165
Christopher Faulet61cc8522020-04-20 14:54:42 +0200166/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200167const char *get_check_status_description(short check_status) {
168
169 const char *desc;
170
171 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200172 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200173 else
174 desc = NULL;
175
176 if (desc && *desc)
177 return desc;
178 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200179 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200180}
181
Christopher Faulet61cc8522020-04-20 14:54:42 +0200182/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200183const char *get_check_status_info(short check_status) {
184
185 const char *info;
186
187 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200188 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200189 else
190 info = NULL;
191
192 if (info && *info)
193 return info;
194 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200195 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200196}
197
Christopher Faulet61cc8522020-04-20 14:54:42 +0200198/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100199const char *get_analyze_status(short analyze_status) {
200
201 const char *desc;
202
203 if (analyze_status < HANA_STATUS_SIZE)
204 desc = analyze_statuses[analyze_status].desc;
205 else
206 desc = NULL;
207
208 if (desc && *desc)
209 return desc;
210 else
211 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
212}
213
Christopher Faulet61cc8522020-04-20 14:54:42 +0200214/* Sets check->status, update check->duration and fill check->result with an
215 * adequate CHK_RES_* value. The new check->health is computed based on the
216 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200217 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200218 * Shows information in logs about failed health check if server is UP or
219 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200220 */
Simon Horman4a741432013-02-23 15:35:38 +0900221static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100222{
Simon Horman4a741432013-02-23 15:35:38 +0900223 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200224 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200225 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900226
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200227 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100228 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900229 check->desc[0] = '\0';
230 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200231 return;
232 }
233
Simon Horman4a741432013-02-23 15:35:38 +0900234 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200235 return;
236
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200237 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900238 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
239 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200240 } else
Simon Horman4a741432013-02-23 15:35:38 +0900241 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200242
Simon Horman4a741432013-02-23 15:35:38 +0900243 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200244 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900245 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200246
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100247 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900248 check->duration = -1;
249 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200250 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900251 check->duration = tv_ms_elapsed(&check->start, &now);
252 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200253 }
254
Willy Tarreau23964182014-05-20 20:56:30 +0200255 /* no change is expected if no state change occurred */
256 if (check->result == CHK_RES_NEUTRAL)
257 return;
258
Olivier Houchard0923fa42019-01-11 18:43:04 +0100259 /* If the check was really just sending a mail, it won't have an
260 * associated server, so we're done now.
261 */
262 if (!s)
263 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200265
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200266 switch (check->result) {
267 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200268 /* Failure to connect to the agent as a secondary check should not
269 * cause the server to be marked down.
270 */
271 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900272 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200273 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100274 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200275 report = 1;
276 check->health--;
277 if (check->health < check->rise)
278 check->health = 0;
279 }
280 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200281
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200282 case CHK_RES_PASSED:
283 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
284 if ((check->health < check->rise + check->fall - 1) &&
285 (check->result == CHK_RES_PASSED || check->health > 0)) {
286 report = 1;
287 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200288
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200289 if (check->health >= check->rise)
290 check->health = check->rise + check->fall - 1; /* OK now */
291 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200292
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200293 /* clear consecutive_errors if observing is enabled */
294 if (s->onerror)
295 s->consecutive_errors = 0;
296 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100297
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200298 default:
299 break;
300 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200301
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200302 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
303 (status != prev_status || report)) {
304 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200305 "%s check for %sserver %s/%s %s%s",
306 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200307 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100308 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100309 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200310 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311
Emeric Brun5a133512017-10-19 14:42:30 +0200312 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200313
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100314 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200315 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
316 (check->health >= check->rise) ? check->fall : check->rise,
317 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200318
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200319 ha_warning("%s.\n", trash.area);
320 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
321 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200322 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200323}
324
Willy Tarreau4eec5472014-05-20 22:32:27 +0200325/* Marks the check <check>'s server down if the current check is already failed
326 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200327 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200328static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200329{
Simon Horman4a741432013-02-23 15:35:38 +0900330 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900331
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200332 /* The agent secondary check should only cause a server to be marked
333 * as down if check->status is HCHK_STATUS_L7STS, which indicates
334 * that the agent returned "fail", "stopped" or "down".
335 * The implication here is that failure to connect to the agent
336 * as a secondary check should not cause the server to be marked
337 * down. */
338 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
339 return;
340
Willy Tarreau4eec5472014-05-20 22:32:27 +0200341 if (check->health > 0)
342 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100343
Willy Tarreau4eec5472014-05-20 22:32:27 +0200344 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200345 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200346}
347
Willy Tarreauaf549582014-05-16 17:37:50 +0200348/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200349 * it isn't in maintenance, it is not tracking a down server and other checks
350 * comply. The rule is simple : by default, a server is up, unless any of the
351 * following conditions is true :
352 * - health check failed (check->health < rise)
353 * - agent check failed (agent->health < rise)
354 * - the server tracks a down server (track && track->state == STOPPED)
355 * Note that if the server has a slowstart, it will switch to STARTING instead
356 * of RUNNING. Also, only the health checks support the nolb mode, so the
357 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200358 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200359static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200360{
Simon Horman4a741432013-02-23 15:35:38 +0900361 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100362
Emeric Brun52a91d32017-08-31 14:41:55 +0200363 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200364 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100365
Emeric Brun52a91d32017-08-31 14:41:55 +0200366 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200367 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368
Willy Tarreau3e048382014-05-21 10:30:54 +0200369 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
370 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100371
Willy Tarreau3e048382014-05-21 10:30:54 +0200372 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
373 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200374
Emeric Brun52a91d32017-08-31 14:41:55 +0200375 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200376 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100377
Emeric Brun5a133512017-10-19 14:42:30 +0200378 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100379}
380
Willy Tarreaudb58b792014-05-21 13:57:23 +0200381/* Marks the check <check> as valid and tries to set its server into stopping mode
382 * if it was running or starting, and provided it isn't in maintenance and other
383 * checks comply. The conditions for the server to be marked in stopping mode are
384 * the same as for it to be turned up. Also, only the health checks support the
385 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200386 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200387static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200388{
Simon Horman4a741432013-02-23 15:35:38 +0900389 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100390
Emeric Brun52a91d32017-08-31 14:41:55 +0200391 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200392 return;
393
Willy Tarreaudb58b792014-05-21 13:57:23 +0200394 if (check->state & CHK_ST_AGENT)
395 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396
Emeric Brun52a91d32017-08-31 14:41:55 +0200397 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200398 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100399
Willy Tarreaudb58b792014-05-21 13:57:23 +0200400 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
401 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100402
Willy Tarreaudb58b792014-05-21 13:57:23 +0200403 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
404 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100405
Willy Tarreaub26881a2017-12-23 11:16:49 +0100406 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100407}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200408
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100409/* note: use health_adjust() only, which first checks that the observe mode is
410 * enabled.
411 */
412void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100413{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100414 int failed;
415 int expire;
416
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100417 if (s->observe >= HANA_OBS_SIZE)
418 return;
419
Willy Tarreaubb956662013-01-24 00:37:39 +0100420 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100421 return;
422
423 switch (analyze_statuses[status].lr[s->observe - 1]) {
424 case 1:
425 failed = 1;
426 break;
427
428 case 2:
429 failed = 0;
430 break;
431
432 default:
433 return;
434 }
435
436 if (!failed) {
437 /* good: clear consecutive_errors */
438 s->consecutive_errors = 0;
439 return;
440 }
441
Olivier Houchard7059c552019-03-08 18:49:32 +0100442 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100443
444 if (s->consecutive_errors < s->consecutive_errors_limit)
445 return;
446
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100447 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
448 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100449
450 switch (s->onerror) {
451 case HANA_ONERR_FASTINTER:
452 /* force fastinter - nothing to do here as all modes force it */
453 break;
454
455 case HANA_ONERR_SUDDTH:
456 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900457 if (s->check.health > s->check.rise)
458 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100459
460 /* no break - fall through */
461
462 case HANA_ONERR_FAILCHK:
463 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200464 set_server_check_status(&s->check, HCHK_STATUS_HANA,
465 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200466 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100467 break;
468
469 case HANA_ONERR_MARKDWN:
470 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900471 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200472 set_server_check_status(&s->check, HCHK_STATUS_HANA,
473 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200474 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100475 break;
476
477 default:
478 /* write a warning? */
479 break;
480 }
481
482 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100483 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100484
Simon Horman66183002013-02-23 10:16:43 +0900485 if (s->check.fastinter) {
486 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300487 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200488 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300489 /* requeue check task with new expire */
490 task_queue(s->check.task);
491 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100492 }
Willy Tarreauef781042010-01-27 11:53:01 +0100493}
494
Christopher Faulet61cc8522020-04-20 14:54:42 +0200495/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100496 * closed, keep errno intact as it is supposed to contain the valid error code.
497 * If no error is reported, check the socket's error queue using getsockopt().
498 * Warning, this must be done only once when returning from poll, and never
499 * after an I/O error was attempted, otherwise the error queue might contain
500 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
501 * socket. Returns non-zero if an error was reported, zero if everything is
502 * clean (including a properly closed socket).
503 */
504static int retrieve_errno_from_socket(struct connection *conn)
505{
506 int skerr;
507 socklen_t lskerr = sizeof(skerr);
508
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100509 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100510 return 1;
511
Willy Tarreau3c728722014-01-23 13:50:42 +0100512 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100513 return 0;
514
Willy Tarreau585744b2017-08-24 14:31:19 +0200515 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100516 errno = skerr;
517
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100518 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100519
520 if (!errno) {
521 /* we could not retrieve an error, that does not mean there is
522 * none. Just don't change anything and only report the prior
523 * error if any.
524 */
525 if (conn->flags & CO_FL_ERROR)
526 return 1;
527 else
528 return 0;
529 }
530
531 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
532 return 1;
533}
534
Christopher Faulet61cc8522020-04-20 14:54:42 +0200535/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100536 * and adjust the server status accordingly. It may make use of <errno_bck>
537 * if non-null when the caller is absolutely certain of its validity (eg:
538 * checked just after a syscall). If the caller doesn't have a valid errno,
539 * it can pass zero, and retrieve_errno_from_socket() will be called to try
540 * to extract errno from the socket. If no error is reported, it will consider
541 * the <expired> flag. This is intended to be used when a connection error was
542 * reported in conn->flags or when a timeout was reported in <expired>. The
543 * function takes care of not updating a server status which was already set.
544 * All situations where at least one of <expired> or CO_FL_ERROR are set
545 * produce a status.
546 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200547static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200549 struct conn_stream *cs = check->cs;
550 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100551 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200552 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200553 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100555 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100556 return;
557
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100558 errno = unclean_errno(errno_bck);
559 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100560 retrieve_errno_from_socket(conn);
561
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200562 if (conn && !(conn->flags & CO_FL_ERROR) &&
563 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100564 return;
565
566 /* we'll try to build a meaningful error message depending on the
567 * context of the error possibly present in conn->err_code, and the
568 * socket error possibly collected above. This is useful to know the
569 * exact step of the L6 layer (eg: SSL handshake).
570 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200571 chk = get_trash_chunk();
572
Christopher Faulet799f3a42020-04-07 12:06:14 +0200573 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200574 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200575 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200576 if (!step)
577 chunk_printf(chk, " at initial connection step of tcp-check");
578 else {
579 chunk_printf(chk, " at step %d of tcp-check", step);
580 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200581 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
582 if (check->current_step->connect.port)
583 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200584 else
585 chunk_appendf(chk, " (connect)");
586 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200587 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
588 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100589
590 switch (expect->type) {
591 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200592 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100593 break;
594 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200595 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100596 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200597 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200598 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100599 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200600 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100601 chunk_appendf(chk, " (expect binary regex)");
602 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200603 case TCPCHK_EXPECT_STRING_LF:
604 chunk_appendf(chk, " (expect log-format string)");
605 break;
606 case TCPCHK_EXPECT_BINARY_LF:
607 chunk_appendf(chk, " (expect log-format binary)");
608 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200610 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200612 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200613 chunk_appendf(chk, " (expect HTTP status regex)");
614 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200615 case TCPCHK_EXPECT_HTTP_HEADER:
616 chunk_appendf(chk, " (expect HTTP header pattern)");
617 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200618 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200619 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200620 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200621 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200622 chunk_appendf(chk, " (expect HTTP body regex)");
623 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200624 case TCPCHK_EXPECT_HTTP_BODY_LF:
625 chunk_appendf(chk, " (expect log-format HTTP body)");
626 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200627 case TCPCHK_EXPECT_CUSTOM:
628 chunk_appendf(chk, " (expect custom function)");
629 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100630 case TCPCHK_EXPECT_UNDEF:
631 chunk_appendf(chk, " (undefined expect!)");
632 break;
633 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200634 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200635 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200636 chunk_appendf(chk, " (send)");
637 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200638
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200639 if (check->current_step && check->current_step->comment)
640 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200641 }
642 }
643
Willy Tarreau00149122017-10-04 18:05:01 +0200644 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100645 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200646 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
647 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100648 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
650 chk->area);
651 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100652 }
653 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100654 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200655 chunk_printf(&trash, "%s%s", strerror(errno),
656 chk->area);
657 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100658 }
659 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200660 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100661 }
662 }
663
Willy Tarreau00149122017-10-04 18:05:01 +0200664 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200665 /* NOTE: this is reported after <fall> tries */
666 chunk_printf(chk, "No port available for the TCP connection");
667 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
668 }
669
Willy Tarreau00149122017-10-04 18:05:01 +0200670 if (!conn) {
671 /* connection allocation error before the connection was established */
672 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
673 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100674 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100675 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200676 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100677 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
678 else if (expired)
679 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200680
681 /*
682 * might be due to a server IP change.
683 * Let's trigger a DNS resolution if none are currently running.
684 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100685 if (check->server)
686 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200687
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100688 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100689 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100690 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200691 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100692 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
693 else if (expired)
694 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
695 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200696 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 /* I/O error after connection was established and before we could diagnose */
698 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
699 }
700 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200701 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
702
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100703 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200704 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
705 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200706 tout = check->current_step->expect.tout_status;
707 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100708 }
709
710 return;
711}
712
Willy Tarreaubaaee002006-06-26 02:48:02 +0200713
Christopher Faulet61cc8522020-04-20 14:54:42 +0200714/**************************************************************************/
715/*************** Init/deinit tcp-check rules and ruleset ******************/
716/**************************************************************************/
717/* Releases memory allocated for a log-format string */
718static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200719{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200720 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100721
Christopher Faulet61cc8522020-04-20 14:54:42 +0200722 list_for_each_entry_safe(lf, lfb, fmt, list) {
723 LIST_DEL(&lf->list);
724 release_sample_expr(lf->expr);
725 free(lf->arg);
726 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100727 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200728}
729
Christopher Faulet61cc8522020-04-20 14:54:42 +0200730/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
731static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100732{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200733 if (!hdr)
734 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200735
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200737 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200738 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100739}
740
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741/* Releases memory allocated for an HTTP header list used in a tcp-check send
742 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200743 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200744static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200745{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200746 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200747
Christopher Faulet61cc8522020-04-20 14:54:42 +0200748 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
749 LIST_DEL(&hdr->list);
750 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200751 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200752}
753
Christopher Faulet61cc8522020-04-20 14:54:42 +0200754/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
755 * tcp-check was allocated using a memory pool (it is used to instantiate email
756 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200757 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200758static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200759{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200760 if (!rule)
761 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200762
Christopher Faulet61cc8522020-04-20 14:54:42 +0200763 free(rule->comment);
764 switch (rule->action) {
765 case TCPCHK_ACT_SEND:
766 switch (rule->send.type) {
767 case TCPCHK_SEND_STRING:
768 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200769 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200770 break;
771 case TCPCHK_SEND_STRING_LF:
772 case TCPCHK_SEND_BINARY_LF:
773 free_tcpcheck_fmt(&rule->send.fmt);
774 break;
775 case TCPCHK_SEND_HTTP:
776 free(rule->send.http.meth.str.area);
777 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200778 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200779 else
780 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200781 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200782 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
783 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200784 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200785 else
786 free_tcpcheck_fmt(&rule->send.http.body_fmt);
787 break;
788 case TCPCHK_SEND_UNDEF:
789 break;
790 }
791 break;
792 case TCPCHK_ACT_EXPECT:
793 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
794 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
795 release_sample_expr(rule->expect.status_expr);
796 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200797 case TCPCHK_EXPECT_HTTP_STATUS:
798 free(rule->expect.codes.codes);
799 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200800 case TCPCHK_EXPECT_STRING:
801 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200802 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200803 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200804 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200805 case TCPCHK_EXPECT_STRING_REGEX:
806 case TCPCHK_EXPECT_BINARY_REGEX:
807 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
808 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200809 regex_free(rule->expect.regex);
810 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200811 case TCPCHK_EXPECT_STRING_LF:
812 case TCPCHK_EXPECT_BINARY_LF:
813 case TCPCHK_EXPECT_HTTP_BODY_LF:
814 free_tcpcheck_fmt(&rule->expect.fmt);
815 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200816 case TCPCHK_EXPECT_HTTP_HEADER:
817 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
818 regex_free(rule->expect.hdr.name_re);
819 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
820 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
821 else
822 istfree(&rule->expect.hdr.name);
823
824 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
825 regex_free(rule->expect.hdr.value_re);
826 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
827 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
828 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
829 istfree(&rule->expect.hdr.value);
830 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200831 case TCPCHK_EXPECT_CUSTOM:
832 case TCPCHK_EXPECT_UNDEF:
833 break;
834 }
835 break;
836 case TCPCHK_ACT_CONNECT:
837 free(rule->connect.sni);
838 free(rule->connect.alpn);
839 release_sample_expr(rule->connect.port_expr);
840 break;
841 case TCPCHK_ACT_COMMENT:
842 break;
843 case TCPCHK_ACT_ACTION_KW:
844 free(rule->action_kw.rule);
845 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200846 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200847
848 if (in_pool)
849 pool_free(pool_head_tcpcheck_rule, rule);
850 else
851 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200852}
853
Christopher Faulet61cc8522020-04-20 14:54:42 +0200854/* Creates a tcp-check variable used in preset variables before executing a
855 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100856 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200857static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100858{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200859 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100860
Christopher Faulet61cc8522020-04-20 14:54:42 +0200861 var = calloc(1, sizeof(*var));
862 if (var == NULL)
863 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100864
Christopher Fauletb61caf42020-04-21 10:57:42 +0200865 var->name = istdup(name);
866 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200867 free(var);
868 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100869 }
Simon Horman98637e52014-06-20 12:30:16 +0900870
Christopher Faulet61cc8522020-04-20 14:54:42 +0200871 LIST_INIT(&var->list);
872 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900873}
874
Christopher Faulet61cc8522020-04-20 14:54:42 +0200875/* Releases memory allocated for a preset tcp-check variable */
876static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900877{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200878 if (!var)
879 return;
880
Christopher Fauletb61caf42020-04-21 10:57:42 +0200881 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200882 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
883 free(var->data.u.str.area);
884 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
885 free(var->data.u.meth.str.area);
886 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900887}
888
Christopher Faulet61cc8522020-04-20 14:54:42 +0200889/* Releases a list of preset tcp-check variables */
890static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900891{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200892 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200893
Christopher Faulet61cc8522020-04-20 14:54:42 +0200894 list_for_each_entry_safe(var, back, vars, list) {
895 LIST_DEL(&var->list);
896 free_tcpcheck_var(var);
897 }
Simon Horman98637e52014-06-20 12:30:16 +0900898}
899
Christopher Faulet61cc8522020-04-20 14:54:42 +0200900/* Duplicate a list of preset tcp-check variables */
901int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900902{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900904
Christopher Faulet61cc8522020-04-20 14:54:42 +0200905 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200906 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200907 if (!new)
908 goto error;
909 new->data.type = var->data.type;
910 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
911 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
912 goto error;
913 if (var->data.type == SMP_T_STR)
914 new->data.u.str.area[new->data.u.str.data] = 0;
915 }
916 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
917 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
918 goto error;
919 new->data.u.str.area[new->data.u.str.data] = 0;
920 new->data.u.meth.meth = var->data.u.meth.meth;
921 }
922 else
923 new->data.u = var->data.u;
924 LIST_ADDQ(dst, &new->list);
925 }
926 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900927
Christopher Faulet61cc8522020-04-20 14:54:42 +0200928 error:
929 free(new);
930 return 0;
931}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200932
Christopher Faulet61cc8522020-04-20 14:54:42 +0200933/* Looks for a shared tcp-check ruleset given its name. */
934static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
935{
936 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200937 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900938
Christopher Fauletd7cee712020-04-21 13:45:00 +0200939 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
940 if (node) {
941 rs = container_of(node, typeof(*rs), node);
942 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200943 }
944 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900945}
946
Christopher Faulet61cc8522020-04-20 14:54:42 +0200947/* Creates a new shared tcp-check ruleset */
948static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900949{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900951
Christopher Faulet61cc8522020-04-20 14:54:42 +0200952 rs = calloc(1, sizeof(*rs));
953 if (rs == NULL)
954 return NULL;
955
Christopher Fauletd7cee712020-04-21 13:45:00 +0200956 rs->node.key = strdup(name);
957 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200958 free(rs);
959 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900960 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200963 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900965}
966
Christopher Faulet61cc8522020-04-20 14:54:42 +0200967/* Releases memory allocated by a tcp-check ruleset. */
968static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900969{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200970 struct tcpcheck_rule *r, *rb;
971 if (!rs)
972 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200973
Christopher Fauletd7cee712020-04-21 13:45:00 +0200974 ebpt_delete(&rs->node);
975 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200976 list_for_each_entry_safe(r, rb, &rs->rules, list) {
977 LIST_DEL(&r->list);
978 free_tcpcheck(r, 0);
979 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200980 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900981}
982
Christopher Faulet61cc8522020-04-20 14:54:42 +0200983
984/**************************************************************************/
985/**************** Everything about tcp-checks execution *******************/
986/**************************************************************************/
987/* Returns the id of a step in a tcp-check ruleset */
988static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200989{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200990 if (!rule)
991 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900992
Christopher Faulet61cc8522020-04-20 14:54:42 +0200993 /* no last started step => first step */
994 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900995 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900996
Christopher Faulet61cc8522020-04-20 14:54:42 +0200997 /* last step is the first implicit connect */
998 if (rule->index == 0 &&
999 rule->action == TCPCHK_ACT_CONNECT &&
1000 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1001 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001002
Christopher Faulet61cc8522020-04-20 14:54:42 +02001003 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001004}
1005
Christopher Faulet61cc8522020-04-20 14:54:42 +02001006/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1007 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001008 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001010{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001011 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001012
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 list_for_each_entry(r, rules->list, list) {
1014 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1015 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001016 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001017 return NULL;
1018}
Cyril Bontéac92a062014-12-27 22:28:38 +01001019
Christopher Faulet61cc8522020-04-20 14:54:42 +02001020/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1021 * NULL if none was found.
1022 */
1023static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1024{
1025 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001026
Christopher Faulet61cc8522020-04-20 14:54:42 +02001027 list_for_each_entry_rev(r, rules->list, list) {
1028 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1029 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001030 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001031 return NULL;
1032}
Cyril Bontéac92a062014-12-27 22:28:38 +01001033
Christopher Faulet61cc8522020-04-20 14:54:42 +02001034/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1035 * <start> or NULL if non was found. If <start> is NULL, it relies on
1036 * get_first_tcpcheck_rule().
1037 */
1038static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1039{
1040 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001041
Christopher Faulet61cc8522020-04-20 14:54:42 +02001042 if (!start)
1043 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001044
Christopher Faulet61cc8522020-04-20 14:54:42 +02001045 r = LIST_NEXT(&start->list, typeof(r), list);
1046 list_for_each_entry_from(r, rules->list, list) {
1047 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1048 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001049 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001051}
Simon Horman98637e52014-06-20 12:30:16 +09001052
Simon Horman98637e52014-06-20 12:30:16 +09001053
Christopher Faulet61cc8522020-04-20 14:54:42 +02001054/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1055static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1056 int match, struct ist info)
1057{
1058 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001059
Christopher Faulet61cc8522020-04-20 14:54:42 +02001060 /* Follows these step to produce the info message:
1061 * 1. if info field is already provided, copy it
1062 * 2. if the expect rule provides an onerror log-format string,
1063 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001064 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001065 * 4. Otherwise produce the generic tcp-check info message
1066 */
1067 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001068 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001070 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001071 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1072 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1073 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001074 }
Simon Horman98637e52014-06-20 12:30:16 +09001075
Christopher Faulet61cc8522020-04-20 14:54:42 +02001076 if (check->type == PR_O2_TCPCHK_CHK &&
1077 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1078 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001079
Christopher Faulet61cc8522020-04-20 14:54:42 +02001080 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1081 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001082 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001083 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1084 break;
1085 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001087 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001088 tcpcheck_get_step_id(check, rule));
1089 break;
1090 case TCPCHK_EXPECT_BINARY:
1091 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1092 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001093 case TCPCHK_EXPECT_STRING_REGEX:
1094 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1095 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001096 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1097 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001098 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001100 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001101 case TCPCHK_EXPECT_STRING_LF:
1102 case TCPCHK_EXPECT_HTTP_BODY_LF:
1103 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1104 break;
1105 case TCPCHK_EXPECT_BINARY_LF:
1106 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1107 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001108 case TCPCHK_EXPECT_CUSTOM:
1109 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1110 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001111 case TCPCHK_EXPECT_HTTP_HEADER:
1112 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001113 case TCPCHK_EXPECT_UNDEF:
1114 /* Should never happen. */
1115 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001116 }
1117
Christopher Faulet61cc8522020-04-20 14:54:42 +02001118 comment:
1119 /* If the failing expect rule provides a comment, it is concatenated to
1120 * the info message.
1121 */
1122 if (rule->comment) {
1123 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001124 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001125 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001126
Christopher Faulet61cc8522020-04-20 14:54:42 +02001127 /* Finally, the check status code is set if the failing expect rule
1128 * defines a status expression.
1129 */
1130 if (rule->expect.status_expr) {
1131 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001132 rule->expect.status_expr, SMP_T_STR);
1133
1134 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1135 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001137 }
Simon Horman98637e52014-06-20 12:30:16 +09001138
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 *(b_tail(msg)) = '\0';
1140}
Cyril Bontéac92a062014-12-27 22:28:38 +01001141
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1143static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1144 struct ist info)
1145{
1146 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001147
Christopher Faulet61cc8522020-04-20 14:54:42 +02001148 /* Follows these step to produce the info message:
1149 * 1. if info field is already provided, copy it
1150 * 2. if the expect rule provides an onsucces log-format string,
1151 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001152 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001153 * 4. Otherwise produce the generic tcp-check info message
1154 */
1155 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001156 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001157 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1158 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1159 &rule->expect.onsuccess_fmt);
1160 else if (check->type == PR_O2_TCPCHK_CHK &&
1161 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1162 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001163
Christopher Faulet61cc8522020-04-20 14:54:42 +02001164 /* Finally, the check status code is set if the expect rule defines a
1165 * status expression.
1166 */
1167 if (rule->expect.status_expr) {
1168 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001169 rule->expect.status_expr, SMP_T_STR);
1170
1171 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1172 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001173 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001174 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001175
1176 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001177}
1178
Christopher Faulet61cc8522020-04-20 14:54:42 +02001179/* Builds the server state header used by HTTP health-checks */
1180static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001181{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001182 int sv_state;
1183 int ratio;
1184 char addr[46];
1185 char port[6];
1186 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1187 "UP %d/%d", "UP",
1188 "NOLB %d/%d", "NOLB",
1189 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001190
Christopher Faulet61cc8522020-04-20 14:54:42 +02001191 if (!(s->check.state & CHK_ST_ENABLED))
1192 sv_state = 6;
1193 else if (s->cur_state != SRV_ST_STOPPED) {
1194 if (s->check.health == s->check.rise + s->check.fall - 1)
1195 sv_state = 3; /* UP */
1196 else
1197 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001198
Christopher Faulet61cc8522020-04-20 14:54:42 +02001199 if (s->cur_state == SRV_ST_STOPPING)
1200 sv_state += 2;
1201 } else {
1202 if (s->check.health)
1203 sv_state = 1; /* going up */
1204 else
1205 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001206 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001207
Christopher Faulet61cc8522020-04-20 14:54:42 +02001208 chunk_appendf(buf, srv_hlt_st[sv_state],
1209 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1210 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001211
Christopher Faulet61cc8522020-04-20 14:54:42 +02001212 addr_to_str(&s->addr, addr, sizeof(addr));
1213 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1214 snprintf(port, sizeof(port), "%u", s->svc_port);
1215 else
1216 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001217
Christopher Faulet61cc8522020-04-20 14:54:42 +02001218 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1219 addr, port, s->proxy->id, s->id,
1220 global.node,
1221 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1222 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1223 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1224 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001225
Christopher Faulet61cc8522020-04-20 14:54:42 +02001226 if ((s->cur_state == SRV_ST_STARTING) &&
1227 now.tv_sec < s->last_change + s->slowstart &&
1228 now.tv_sec >= s->last_change) {
1229 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1230 chunk_appendf(buf, "; throttle=%d%%", ratio);
1231 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 return b_data(buf);
1234}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236/* Internal functions to parse and validate a MySQL packet in the context of an
1237 * expect rule. It start to parse the input buffer at the offset <offset>. If
1238 * <last_read> is set, no more data are expected.
1239 */
1240static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1241 unsigned int offset, int last_read)
1242{
1243 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1244 enum healthcheck_status status;
1245 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001246 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001247 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001248
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001249
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001251 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001252 if (!last_read)
1253 goto wait_more_data;
1254
1255 /* invalid length or truncated response */
1256 status = HCHK_STATUS_L7RSP;
1257 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001258 }
1259
Christopher Faulet61cc8522020-04-20 14:54:42 +02001260 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1261 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1262 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001263
Christopher Faulet61cc8522020-04-20 14:54:42 +02001264 if (b_data(&check->bi) < offset+plen+4) {
1265 if (!last_read)
1266 goto wait_more_data;
1267
1268 /* invalid length or truncated response */
1269 status = HCHK_STATUS_L7RSP;
1270 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001271 }
Simon Horman98637e52014-06-20 12:30:16 +09001272
Christopher Faulet61cc8522020-04-20 14:54:42 +02001273 if (*b_peek(&check->bi, offset+4) == '\xff') {
1274 /* MySQL Error packet always begin with field_count = 0xff */
1275 status = HCHK_STATUS_L7STS;
1276 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1277 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1278 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1279 goto error;
1280 }
Simon Horman98637e52014-06-20 12:30:16 +09001281
Christopher Faulet61cc8522020-04-20 14:54:42 +02001282 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1283 /* Not the last rule, continue */
1284 goto out;
1285 }
Simon Horman98637e52014-06-20 12:30:16 +09001286
Christopher Faulet61cc8522020-04-20 14:54:42 +02001287 /* We set the MySQL Version in description for information purpose
1288 * FIXME : it can be cool to use MySQL Version for other purpose,
1289 * like mark as down old MySQL server.
1290 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001291 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1292 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001293
Christopher Faulet61cc8522020-04-20 14:54:42 +02001294 out:
1295 free_trash_chunk(msg);
1296 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001297
Christopher Faulet61cc8522020-04-20 14:54:42 +02001298 error:
1299 ret = TCPCHK_EVAL_STOP;
1300 check->code = err;
1301 msg = alloc_trash_chunk();
1302 if (msg)
1303 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1304 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1305 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001306
Christopher Faulet61cc8522020-04-20 14:54:42 +02001307 wait_more_data:
1308 ret = TCPCHK_EVAL_WAIT;
1309 goto out;
1310}
Simon Horman98637e52014-06-20 12:30:16 +09001311
Christopher Faulet61cc8522020-04-20 14:54:42 +02001312/* Custom tcp-check expect function to parse and validate the MySQL initial
1313 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1314 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1315 * error occurred.
1316 */
1317static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1318{
1319 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1320}
Simon Horman98637e52014-06-20 12:30:16 +09001321
Christopher Faulet61cc8522020-04-20 14:54:42 +02001322/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1323 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1324 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1325 * an error occurred.
1326 */
1327static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1328{
1329 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001330
Christopher Faulet61cc8522020-04-20 14:54:42 +02001331 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1332 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1333 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1336}
Simon Horman98637e52014-06-20 12:30:16 +09001337
Christopher Faulet61cc8522020-04-20 14:54:42 +02001338/* Custom tcp-check expect function to parse and validate the LDAP bind response
1339 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1340 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1341 * error occurred.
1342 */
1343static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1344{
1345 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1346 enum healthcheck_status status;
1347 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001348 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001350
Christopher Faulet61cc8522020-04-20 14:54:42 +02001351 /* Check if the server speaks LDAP (ASN.1/BER)
1352 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1353 * http://tools.ietf.org/html/rfc4511
1354 */
1355 /* size of LDAPMessage */
1356 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001357
Christopher Faulet61cc8522020-04-20 14:54:42 +02001358 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1359 * messageID: 0x02 0x01 0x01: INTEGER 1
1360 * protocolOp: 0x61: bindResponse
1361 */
1362 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1363 status = HCHK_STATUS_L7RSP;
1364 desc = ist("Not LDAPv3 protocol");
1365 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001366 }
Simon Horman98637e52014-06-20 12:30:16 +09001367
Christopher Faulet61cc8522020-04-20 14:54:42 +02001368 /* size of bindResponse */
1369 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001370
Christopher Faulet61cc8522020-04-20 14:54:42 +02001371 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1372 * ldapResult: 0x0a 0x01: ENUMERATION
1373 */
1374 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1375 status = HCHK_STATUS_L7RSP;
1376 desc = ist("Not LDAPv3 protocol");
1377 goto error;
1378 }
Simon Horman98637e52014-06-20 12:30:16 +09001379
Christopher Faulet61cc8522020-04-20 14:54:42 +02001380 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1381 * resultCode
1382 */
1383 check->code = *(b_head(&check->bi) + msglen + 9);
1384 if (check->code) {
1385 status = HCHK_STATUS_L7STS;
1386 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1387 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001388 }
1389
Christopher Faulet1941bab2020-05-05 07:55:50 +02001390 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1391 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001392
Christopher Faulet61cc8522020-04-20 14:54:42 +02001393 out:
1394 free_trash_chunk(msg);
1395 return ret;
1396
1397 error:
1398 ret = TCPCHK_EVAL_STOP;
1399 msg = alloc_trash_chunk();
1400 if (msg)
1401 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1402 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1403 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001404}
1405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1407 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1408 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001409 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001411{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1413 enum healthcheck_status status;
1414 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001415 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001417
Willy Tarreaubaaee002006-06-26 02:48:02 +02001418
Christopher Faulet61cc8522020-04-20 14:54:42 +02001419 memcpy(&framesz, b_head(&check->bi), 4);
1420 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001421
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 if (!last_read && b_data(&check->bi) < (4+framesz))
1423 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001424
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425 memset(b_orig(&trash), 0, b_size(&trash));
1426 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1427 status = HCHK_STATUS_L7RSP;
1428 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1429 goto error;
1430 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001431
Christopher Faulet1941bab2020-05-05 07:55:50 +02001432 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1433 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001434
Christopher Faulet61cc8522020-04-20 14:54:42 +02001435 out:
1436 free_trash_chunk(msg);
1437 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001438
Christopher Faulet61cc8522020-04-20 14:54:42 +02001439 error:
1440 ret = TCPCHK_EVAL_STOP;
1441 msg = alloc_trash_chunk();
1442 if (msg)
1443 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1444 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1445 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001446
Christopher Faulet61cc8522020-04-20 14:54:42 +02001447 wait_more_data:
1448 ret = TCPCHK_EVAL_WAIT;
1449 goto out;
1450}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001451
Christopher Faulet61cc8522020-04-20 14:54:42 +02001452/* Custom tcp-check expect function to parse and validate the agent-check
1453 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1454 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1455 */
1456static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1457{
1458 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1459 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1460 const char *hs = NULL; /* health status */
1461 const char *as = NULL; /* admin status */
1462 const char *ps = NULL; /* performance status */
1463 const char *cs = NULL; /* maxconn */
1464 const char *err = NULL; /* first error to report */
1465 const char *wrn = NULL; /* first warning to report */
1466 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001467
Christopher Faulet61cc8522020-04-20 14:54:42 +02001468 /* We're getting an agent check response. The agent could
1469 * have been disabled in the mean time with a long check
1470 * still pending. It is important that we ignore the whole
1471 * response.
1472 */
1473 if (!(check->state & CHK_ST_ENABLED))
1474 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001475
Christopher Faulet61cc8522020-04-20 14:54:42 +02001476 /* The agent supports strings made of a single line ended by the
1477 * first CR ('\r') or LF ('\n'). This line is composed of words
1478 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1479 * line may optionally contained a description of a state change
1480 * after a sharp ('#'), which is only considered if a health state
1481 * is announced.
1482 *
1483 * Words may be composed of :
1484 * - a numeric weight suffixed by the percent character ('%').
1485 * - a health status among "up", "down", "stopped", and "fail".
1486 * - an admin status among "ready", "drain", "maint".
1487 *
1488 * These words may appear in any order. If multiple words of the
1489 * same category appear, the last one wins.
1490 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001491
Christopher Faulet61cc8522020-04-20 14:54:42 +02001492 p = b_head(&check->bi);
1493 while (*p && *p != '\n' && *p != '\r')
1494 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001495
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496 if (!*p) {
1497 if (!last_read)
1498 goto wait_more_data;
1499
1500 /* at least inform the admin that the agent is mis-behaving */
1501 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1502 goto out;
1503 }
1504
1505 *p = 0;
1506 cmd = b_head(&check->bi);
1507
1508 while (*cmd) {
1509 /* look for next word */
1510 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1511 cmd++;
1512 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001513 }
1514
Christopher Faulet61cc8522020-04-20 14:54:42 +02001515 if (*cmd == '#') {
1516 /* this is the beginning of a health status description,
1517 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001518 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001519 cmd++;
1520 while (*cmd == '\t' || *cmd == ' ')
1521 cmd++;
1522 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001523 }
1524
Christopher Faulet61cc8522020-04-20 14:54:42 +02001525 /* find the end of the word so that we have a null-terminated
1526 * word between <cmd> and <p>.
1527 */
1528 p = cmd + 1;
1529 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1530 p++;
1531 if (*p)
1532 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001533
Christopher Faulet61cc8522020-04-20 14:54:42 +02001534 /* first, health statuses */
1535 if (strcasecmp(cmd, "up") == 0) {
1536 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1537 status = HCHK_STATUS_L7OKD;
1538 hs = cmd;
1539 }
1540 else if (strcasecmp(cmd, "down") == 0) {
1541 check->server->check.health = 0;
1542 status = HCHK_STATUS_L7STS;
1543 hs = cmd;
1544 }
1545 else if (strcasecmp(cmd, "stopped") == 0) {
1546 check->server->check.health = 0;
1547 status = HCHK_STATUS_L7STS;
1548 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001549 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001550 else if (strcasecmp(cmd, "fail") == 0) {
1551 check->server->check.health = 0;
1552 status = HCHK_STATUS_L7STS;
1553 hs = cmd;
1554 }
1555 /* admin statuses */
1556 else if (strcasecmp(cmd, "ready") == 0) {
1557 as = cmd;
1558 }
1559 else if (strcasecmp(cmd, "drain") == 0) {
1560 as = cmd;
1561 }
1562 else if (strcasecmp(cmd, "maint") == 0) {
1563 as = cmd;
1564 }
1565 /* try to parse a weight here and keep the last one */
1566 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1567 ps = cmd;
1568 }
1569 /* try to parse a maxconn here */
1570 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1571 cs = cmd;
1572 }
1573 else {
1574 /* keep a copy of the first error */
1575 if (!err)
1576 err = cmd;
1577 }
1578 /* skip to next word */
1579 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001580 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001581 /* here, cmd points either to \0 or to the beginning of a
1582 * description. Skip possible leading spaces.
1583 */
1584 while (*cmd == ' ' || *cmd == '\n')
1585 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001586
Christopher Faulet61cc8522020-04-20 14:54:42 +02001587 /* First, update the admin status so that we avoid sending other
1588 * possibly useless warnings and can also update the health if
1589 * present after going back up.
1590 */
1591 if (as) {
1592 if (strcasecmp(as, "drain") == 0)
1593 srv_adm_set_drain(check->server);
1594 else if (strcasecmp(as, "maint") == 0)
1595 srv_adm_set_maint(check->server);
1596 else
1597 srv_adm_set_ready(check->server);
1598 }
Simon Horman98637e52014-06-20 12:30:16 +09001599
Christopher Faulet61cc8522020-04-20 14:54:42 +02001600 /* now change weights */
1601 if (ps) {
1602 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001603
Christopher Faulet61cc8522020-04-20 14:54:42 +02001604 msg = server_parse_weight_change_request(check->server, ps);
1605 if (!wrn || !*wrn)
1606 wrn = msg;
1607 }
Simon Horman98637e52014-06-20 12:30:16 +09001608
Christopher Faulet61cc8522020-04-20 14:54:42 +02001609 if (cs) {
1610 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001611
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001613
Christopher Faulet61cc8522020-04-20 14:54:42 +02001614 msg = server_parse_maxconn_change_request(check->server, cs);
1615 if (!wrn || !*wrn)
1616 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001617 }
1618
Christopher Faulet61cc8522020-04-20 14:54:42 +02001619 /* and finally health status */
1620 if (hs) {
1621 /* We'll report some of the warnings and errors we have
1622 * here. Down reports are critical, we leave them untouched.
1623 * Lack of report, or report of 'UP' leaves the room for
1624 * ERR first, then WARN.
1625 */
1626 const char *msg = cmd;
1627 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001628
Christopher Faulet61cc8522020-04-20 14:54:42 +02001629 if (!*msg || status == HCHK_STATUS_L7OKD) {
1630 if (err && *err)
1631 msg = err;
1632 else if (wrn && *wrn)
1633 msg = wrn;
1634 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001635
Christopher Faulet61cc8522020-04-20 14:54:42 +02001636 t = get_trash_chunk();
1637 chunk_printf(t, "via agent : %s%s%s%s",
1638 hs, *msg ? " (" : "",
1639 msg, *msg ? ")" : "");
1640 set_server_check_status(check, status, t->area);
1641 }
1642 else if (err && *err) {
1643 /* No status change but we'd like to report something odd.
1644 * Just report the current state and copy the message.
1645 */
1646 chunk_printf(&trash, "agent reports an error : %s", err);
1647 set_server_check_status(check, status/*check->status*/, trash.area);
1648 }
1649 else if (wrn && *wrn) {
1650 /* No status change but we'd like to report something odd.
1651 * Just report the current state and copy the message.
1652 */
1653 chunk_printf(&trash, "agent warns : %s", wrn);
1654 set_server_check_status(check, status/*check->status*/, trash.area);
1655 }
1656 else
1657 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001658
Christopher Faulet61cc8522020-04-20 14:54:42 +02001659 out:
1660 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001661
Christopher Faulet61cc8522020-04-20 14:54:42 +02001662 wait_more_data:
1663 ret = TCPCHK_EVAL_WAIT;
1664 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001665}
1666
Christopher Faulet61cc8522020-04-20 14:54:42 +02001667/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1668 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1669 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001670 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001671static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001672{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001673 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1674 struct tcpcheck_connect *connect = &rule->connect;
1675 struct proxy *proxy = check->proxy;
1676 struct server *s = check->server;
1677 struct task *t = check->task;
1678 struct conn_stream *cs;
1679 struct connection *conn = NULL;
1680 struct protocol *proto;
1681 struct xprt_ops *xprt;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001682 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001683 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001684
Christopher Faulet61cc8522020-04-20 14:54:42 +02001685 /* For a connect action we'll create a new connection. We may also have
1686 * to kill a previous one. But we don't want to leave *without* a
1687 * connection if we came here from the connection layer, hence with a
1688 * connection. Thus we'll proceed in the following order :
1689 * 1: close but not release previous connection (handled by the caller)
1690 * 2: try to get a new connection
1691 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001692 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001693
Christopher Faulet61cc8522020-04-20 14:54:42 +02001694 /* 2- prepare new connection */
1695 cs = cs_new(NULL);
1696 if (!cs) {
1697 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1698 tcpcheck_get_step_id(check, rule));
1699 if (rule->comment)
1700 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1701 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1702 ret = TCPCHK_EVAL_STOP;
1703 goto out;
1704 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001705
Christopher Faulet61cc8522020-04-20 14:54:42 +02001706 /* 3- release and replace the old one on success */
1707 if (check->cs) {
1708 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001709 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1710 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001711
1712 /* We may have been scheduled to run, and the I/O handler
1713 * expects to have a cs, so remove the tasklet
1714 */
1715 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1716 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001717 }
1718
Christopher Faulet61cc8522020-04-20 14:54:42 +02001719 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001720
Christopher Faulet61cc8522020-04-20 14:54:42 +02001721 check->cs = cs;
1722 conn = cs->conn;
1723 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001724
Christopher Faulet61cc8522020-04-20 14:54:42 +02001725 /* Maybe there were an older connection we were waiting on */
1726 check->wait_list.events = 0;
1727 conn->target = s ? &s->obj_type : &proxy->obj_type;
1728
1729 /* no client address */
1730 if (!sockaddr_alloc(&conn->dst)) {
1731 status = SF_ERR_RESOURCE;
1732 goto fail_check;
1733 }
1734
1735 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001736 * addr if specified on the server. otherwise, use the server addr (it
1737 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001738 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001739 *conn->dst = (is_addr(&connect->addr)
1740 ? connect->addr
1741 : (is_addr(&check->addr) ? check->addr : s->addr));
1742 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001743
Christopher Faulet61cc8522020-04-20 14:54:42 +02001744 port = 0;
1745 if (!port && connect->port)
1746 port = connect->port;
1747 if (!port && connect->port_expr) {
1748 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001749
Christopher Faulet61cc8522020-04-20 14:54:42 +02001750 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1751 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1752 connect->port_expr, SMP_T_SINT);
1753 if (smp)
1754 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001755 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001756 if (!port && is_inet_addr(&connect->addr))
1757 port = get_host_port(&connect->addr);
1758 if (!port && check->port)
1759 port = check->port;
1760 if (!port && is_inet_addr(&check->addr))
1761 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001762 if (!port) {
1763 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001764 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001765 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001766 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001767
Christopher Faulet61cc8522020-04-20 14:54:42 +02001768 xprt = ((connect->options & TCPCHK_OPT_SSL)
1769 ? xprt_get(XPRT_SSL)
1770 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001771
Christopher Faulet61cc8522020-04-20 14:54:42 +02001772 conn_prepare(conn, proto, xprt);
1773 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001774
Christopher Faulet61cc8522020-04-20 14:54:42 +02001775 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001776 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001777 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001778 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001779
Christopher Faulet61cc8522020-04-20 14:54:42 +02001780 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1781 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 if (!next || next->action != TCPCHK_ACT_EXPECT)
1783 flags |= CONNECT_DELACK_ALWAYS;
1784 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001785 }
1786
Christopher Faulet61cc8522020-04-20 14:54:42 +02001787 if (status != SF_ERR_NONE)
1788 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001789
Christopher Faulet61cc8522020-04-20 14:54:42 +02001790 conn->flags |= CO_FL_PRIVATE;
1791 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001792
Christopher Faulet61cc8522020-04-20 14:54:42 +02001793 /* The mux may be initialized now if there isn't server attached to the
1794 * check (email alerts) or if there is a mux proto specified or if there
1795 * is no alpn.
1796 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001797 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1798 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001799 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001800
Christopher Faulet61cc8522020-04-20 14:54:42 +02001801 if (connect->mux_proto)
1802 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001803 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001804 mux_ops = check->mux_proto->mux;
1805 else {
1806 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1807 ? PROTO_MODE_HTTP
1808 : PROTO_MODE_TCP);
1809
1810 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001811 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001812 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1813 status = SF_ERR_INTERNAL;
1814 goto fail_check;
1815 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001816 }
1817
Christopher Faulet61cc8522020-04-20 14:54:42 +02001818#ifdef USE_OPENSSL
1819 if (connect->sni)
1820 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001821 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001822 ssl_sock_set_servername(conn, s->check.sni);
1823
1824 if (connect->alpn)
1825 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001826 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001827 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1828#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001829 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001830 conn->send_proxy_ofs = 1;
1831 conn->flags |= CO_FL_SOCKS4;
1832 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001833 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001834 conn->send_proxy_ofs = 1;
1835 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001836 }
1837
Christopher Faulet61cc8522020-04-20 14:54:42 +02001838 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1839 conn->send_proxy_ofs = 1;
1840 conn->flags |= CO_FL_SEND_PROXY;
1841 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001842 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001843 conn->send_proxy_ofs = 1;
1844 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001845 }
1846
Christopher Faulet61cc8522020-04-20 14:54:42 +02001847 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1848 /* Some servers don't like reset on close */
1849 fdtab[cs->conn->handle.fd].linger_risk = 0;
1850 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001851
Christopher Faulet61cc8522020-04-20 14:54:42 +02001852 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1853 if (xprt_add_hs(conn) < 0)
1854 status = SF_ERR_RESOURCE;
1855 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001856
Christopher Faulet61cc8522020-04-20 14:54:42 +02001857 fail_check:
1858 /* It can return one of :
1859 * - SF_ERR_NONE if everything's OK
1860 * - SF_ERR_SRVTO if there are no more servers
1861 * - SF_ERR_SRVCL if the connection was refused by the server
1862 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1863 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1864 * - SF_ERR_INTERNAL for any other purely internal errors
1865 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1866 * Note that we try to prevent the network stack from sending the ACK during the
1867 * connect() when a pure TCP check is used (without PROXY protocol).
1868 */
1869 switch (status) {
1870 case SF_ERR_NONE:
1871 /* we allow up to min(inter, timeout.connect) for a connection
1872 * to establish but only when timeout.check is set as it may be
1873 * to short for a full check otherwise
1874 */
1875 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001876
Christopher Faulet61cc8522020-04-20 14:54:42 +02001877 if (proxy->timeout.check && proxy->timeout.connect) {
1878 int t_con = tick_add(now_ms, proxy->timeout.connect);
1879 t->expire = tick_first(t->expire, t_con);
1880 }
1881 break;
1882 case SF_ERR_SRVTO: /* ETIMEDOUT */
1883 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1884 case SF_ERR_PRXCOND:
1885 case SF_ERR_RESOURCE:
1886 case SF_ERR_INTERNAL:
1887 chk_report_conn_err(check, errno, 0);
1888 ret = TCPCHK_EVAL_STOP;
1889 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001890 }
1891
Christopher Faulet61cc8522020-04-20 14:54:42 +02001892 /* don't do anything until the connection is established */
1893 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001894 if (conn->mux) {
1895 if (next && next->action == TCPCHK_ACT_SEND)
1896 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1897 else
1898 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1899 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001900 ret = TCPCHK_EVAL_WAIT;
1901 goto out;
1902 }
1903
1904 out:
1905 if (conn && check->result == CHK_RES_FAILED)
1906 conn->flags |= CO_FL_ERROR;
1907 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001908}
1909
Christopher Faulet61cc8522020-04-20 14:54:42 +02001910/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1911 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1912 * TCPCHK_EVAL_STOP if an error occurred.
1913 */
1914static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001915{
1916 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001917 struct tcpcheck_send *send = &rule->send;
1918 struct conn_stream *cs = check->cs;
1919 struct connection *conn = cs_conn(cs);
1920 struct buffer *tmp = NULL;
1921 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001922
Christopher Faulet61cc8522020-04-20 14:54:42 +02001923 /* reset the read & write buffer */
1924 b_reset(&check->bi);
1925 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001926
Christopher Faulet61cc8522020-04-20 14:54:42 +02001927 switch (send->type) {
1928 case TCPCHK_SEND_STRING:
1929 case TCPCHK_SEND_BINARY:
1930 if (istlen(send->data) >= b_size(&check->bo)) {
1931 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1932 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1933 tcpcheck_get_step_id(check, rule));
1934 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1935 ret = TCPCHK_EVAL_STOP;
1936 goto out;
1937 }
1938 b_putist(&check->bo, send->data);
1939 break;
1940 case TCPCHK_SEND_STRING_LF:
1941 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1942 if (!b_data(&check->bo))
1943 goto out;
1944 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001945 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001946 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001947
Christopher Faulet61cc8522020-04-20 14:54:42 +02001948 tmp = alloc_trash_chunk();
1949 if (!tmp)
1950 goto error_lf;
1951 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1952 if (!b_data(tmp))
1953 goto out;
1954 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001955 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001956 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001957 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001958 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001959 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001960 case TCPCHK_SEND_HTTP: {
1961 struct htx_sl *sl;
1962 struct ist meth, uri, vsn, clen, body;
1963 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001964
Christopher Faulet61cc8522020-04-20 14:54:42 +02001965 tmp = alloc_trash_chunk();
1966 if (!tmp)
1967 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001968
Christopher Faulet61cc8522020-04-20 14:54:42 +02001969 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1970 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1971 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001972 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1973 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1974 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1975 }
1976 else
1977 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001978 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001979
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001980 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1981 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001982 slflags |= HTX_SL_F_VER_11;
1983 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1984 if (!isttest(send->http.body))
1985 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001986
Christopher Faulet61cc8522020-04-20 14:54:42 +02001987 htx = htx_from_buf(&check->bo);
1988 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1989 if (!sl)
1990 goto error_htx;
1991 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001992 if (!http_update_host(htx, sl, uri))
1993 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001994
Christopher Faulet61cc8522020-04-20 14:54:42 +02001995 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1996 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001997 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001998
Christopher Faulet61cc8522020-04-20 14:54:42 +02001999 list_for_each_entry(hdr, &send->http.hdrs, list) {
2000 chunk_reset(tmp);
2001 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2002 if (!b_data(tmp))
2003 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002004 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2005 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002006 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002007 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2008 if (!http_update_authority(htx, sl, hdr_value))
2009 goto error_htx;
2010 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002011 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002012
Christopher Faulet61cc8522020-04-20 14:54:42 +02002013 }
2014 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2015 chunk_reset(tmp);
2016 httpchk_build_status_header(check->server, tmp);
2017 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2018 goto error_htx;
2019 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002020
2021
2022 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2023 chunk_reset(tmp);
2024 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2025 body = ist2(b_orig(tmp), b_data(tmp));
2026 }
2027 else
2028 body = send->http.body;
2029 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2030
2031 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2032 !htx_add_header(htx, ist("Content-length"), clen))
2033 goto error_htx;
2034
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002035
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002037 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002038 !htx_add_endof(htx, HTX_BLK_EOM))
2039 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002040
Christopher Faulet61cc8522020-04-20 14:54:42 +02002041 htx_to_buf(htx, &check->bo);
2042 break;
2043 }
2044 case TCPCHK_SEND_UNDEF:
2045 /* Should never happen. */
2046 ret = TCPCHK_EVAL_STOP;
2047 goto out;
2048 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002049
Christopher Faulet6d471212020-04-22 11:09:25 +02002050
2051 if (conn->mux->snd_buf(cs, &check->bo,
2052 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002053 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002054 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002055 goto out;
2056 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002057 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002058 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002059 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2060 ret = TCPCHK_EVAL_WAIT;
2061 goto out;
2062 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002063
Christopher Faulet61cc8522020-04-20 14:54:42 +02002064 out:
2065 free_trash_chunk(tmp);
2066 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002067
Christopher Faulet61cc8522020-04-20 14:54:42 +02002068 error_htx:
2069 if (htx) {
2070 htx_reset(htx);
2071 htx_to_buf(htx, &check->bo);
2072 }
2073 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2074 tcpcheck_get_step_id(check, rule));
2075 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2076 ret = TCPCHK_EVAL_STOP;
2077 goto out;
2078
2079 error_lf:
2080 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2081 tcpcheck_get_step_id(check, rule));
2082 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2083 ret = TCPCHK_EVAL_STOP;
2084 goto out;
2085
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002086}
2087
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002088/* Try to receive data before evaluating a tcp-check expect rule. Returns
2089 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002090 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2091 * TCPCHK_EVAL_STOP if an error occurred.
2092 */
2093static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002094{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002095 struct conn_stream *cs = check->cs;
2096 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002097 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002098 size_t max, read, cur_read = 0;
2099 int is_empty;
2100 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002101
Christopher Faulet61cc8522020-04-20 14:54:42 +02002102 if (check->wait_list.events & SUB_RETRY_RECV)
2103 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002104
Christopher Faulet61cc8522020-04-20 14:54:42 +02002105 if (cs->flags & CS_FL_EOS)
2106 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002107
Christopher Faulet61cc8522020-04-20 14:54:42 +02002108 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002109
Christopher Faulet61cc8522020-04-20 14:54:42 +02002110 /* prepare to detect if the mux needs more room */
2111 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002112
Christopher Faulet61cc8522020-04-20 14:54:42 +02002113 while ((cs->flags & CS_FL_RCV_MORE) ||
2114 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2115 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2116 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2117 cur_read += read;
2118 if (!read ||
2119 (cs->flags & CS_FL_WANT_ROOM) ||
2120 (--read_poll <= 0) ||
2121 (read < max && read >= global.tune.recv_enough))
2122 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002123 }
2124
Christopher Faulet61cc8522020-04-20 14:54:42 +02002125 end_recv:
2126 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2127 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2128 /* Report network errors only if we got no other data. Otherwise
2129 * we'll let the upper layers decide whether the response is OK
2130 * or not. It is very common that an RST sent by the server is
2131 * reported as an error just after the last data chunk.
2132 */
2133 goto stop;
2134 }
2135 if (!cur_read) {
2136 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2137 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2138 goto wait_more_data;
2139 }
2140 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002141 int status;
2142
Christopher Faulet61cc8522020-04-20 14:54:42 +02002143 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2144 tcpcheck_get_step_id(check, rule));
2145 if (rule->comment)
2146 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002147
2148 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2149 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002150 goto stop;
2151 }
2152 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002153
2154 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002155 return ret;
2156
Christopher Faulet61cc8522020-04-20 14:54:42 +02002157 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002158 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002159 goto out;
2160
2161 wait_more_data:
2162 ret = TCPCHK_EVAL_WAIT;
2163 goto out;
2164}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002165
Christopher Faulet61cc8522020-04-20 14:54:42 +02002166/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2167 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2168 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2169 * error occurred.
2170 */
2171static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, struct tcpcheck_rule *rule, int last_read)
Christopher Faulet267b01b2020-04-04 10:27:09 +02002172{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002173 struct htx *htx = htxbuf(&check->bi);
2174 struct htx_sl *sl;
2175 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002176 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002177 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002178 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002179 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002180 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002181 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002182
Christopher Faulet61cc8522020-04-20 14:54:42 +02002183 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002184
Christopher Faulet61cc8522020-04-20 14:54:42 +02002185 if (htx->flags & HTX_FL_PARSING_ERROR) {
2186 status = HCHK_STATUS_L7RSP;
2187 goto error;
2188 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002189
Christopher Faulet61cc8522020-04-20 14:54:42 +02002190 if (htx_is_empty(htx)) {
2191 if (last_read) {
2192 status = HCHK_STATUS_L7RSP;
2193 goto error;
2194 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002195 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002196 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002197
Christopher Faulet61cc8522020-04-20 14:54:42 +02002198 sl = http_get_stline(htx);
2199 check->code = sl->info.res.status;
2200
2201 if (check->server &&
2202 (check->server->proxy->options & PR_O_DISABLE404) &&
2203 (check->server->next_state != SRV_ST_STOPPED) &&
2204 (check->code == 404)) {
2205 /* 404 may be accepted as "stopping" only if the server was up */
2206 goto out;
2207 }
2208
2209 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2210 /* Make GCC happy ; initialize match to a failure state. */
2211 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002212 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002213
2214 switch (expect->type) {
2215 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002216 match = 0;
2217 for (i = 0; i < expect->codes.num; i++) {
2218 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2219 sl->info.res.status <= expect->codes.codes[i][1]) {
2220 match = 1;
2221 break;
2222 }
2223 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002224
2225 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002226 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002227 if (LIST_ISEMPTY(&expect->onerror_fmt))
2228 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002229 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002230 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002231 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2232
2233 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002234 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002235 if (LIST_ISEMPTY(&expect->onerror_fmt))
2236 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002237 break;
2238
Christopher Faulet39708192020-05-05 10:47:36 +02002239 case TCPCHK_EXPECT_HTTP_HEADER: {
2240 struct http_hdr_ctx ctx;
2241 struct ist npat, vpat, value;
2242 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2243
2244 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2245 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002246 if (!nbuf) {
2247 status = HCHK_STATUS_L7RSP;
2248 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002249 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002250 }
Christopher Faulet39708192020-05-05 10:47:36 +02002251 nbuf->data = sess_build_logline(check->sess, NULL, b_orig(nbuf), b_size(nbuf), &expect->hdr.name_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002252 if (!b_data(nbuf)) {
2253 status = HCHK_STATUS_L7RSP;
2254 desc = ist("log-format string evaluated to an empty string");
2255 goto error;
2256 }
Christopher Faulet39708192020-05-05 10:47:36 +02002257 npat = ist2(b_orig(nbuf), b_data(nbuf));
2258 }
2259 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2260 npat = expect->hdr.name;
2261
2262 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2263 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002264 if (!vbuf) {
2265 status = HCHK_STATUS_L7RSP;
2266 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002267 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002268 }
Christopher Faulet39708192020-05-05 10:47:36 +02002269 vbuf->data = sess_build_logline(check->sess, NULL, b_orig(vbuf), b_size(vbuf), &expect->hdr.value_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002270 if (!b_data(vbuf)) {
2271 status = HCHK_STATUS_L7RSP;
2272 desc = ist("log-format string evaluated to an empty string");
2273 goto error;
2274 }
Christopher Faulet39708192020-05-05 10:47:36 +02002275 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2276 }
2277 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2278 vpat = expect->hdr.value;
2279
2280 match = 0;
2281 ctx.blk = NULL;
2282 while (1) {
2283 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2284 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2285 if (!http_find_str_header(htx, npat, &ctx, full))
2286 goto end_of_match;
2287 break;
2288 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2289 if (!http_find_pfx_header(htx, npat, &ctx, full))
2290 goto end_of_match;
2291 break;
2292 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2293 if (!http_find_sfx_header(htx, npat, &ctx, full))
2294 goto end_of_match;
2295 break;
2296 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2297 if (!http_find_sub_header(htx, npat, &ctx, full))
2298 goto end_of_match;
2299 break;
2300 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2301 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2302 goto end_of_match;
2303 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002304 default:
2305 /* should never happen */
2306 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002307 }
2308
Christopher Faulet083eff32020-05-07 15:41:39 +02002309 /* A header has matched the name pattern, let's test its
2310 * value now (always defined from there). If there is no
2311 * value pattern, it is a good match.
2312 */
2313
Christopher Faulet39708192020-05-05 10:47:36 +02002314 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2315 match = 1;
2316 goto end_of_match;
2317 }
2318
2319 value = ctx.value;
2320 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2321 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2322 if (isteq(value, vpat)) {
2323 match = 1;
2324 goto end_of_match;
2325 }
2326 break;
2327 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2328 if (istlen(value) < istlen(vpat))
2329 break;
2330 value = ist2(istptr(value), istlen(vpat));
2331 if (isteq(value, vpat)) {
2332 match = 1;
2333 goto end_of_match;
2334 }
2335 break;
2336 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2337 if (istlen(value) < istlen(vpat))
2338 break;
2339 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2340 if (isteq(value, vpat)) {
2341 match = 1;
2342 goto end_of_match;
2343 }
2344 break;
2345 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2346 if (isttest(istist(value, vpat))) {
2347 match = 1;
2348 goto end_of_match;
2349 }
2350 break;
2351 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2352 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2353 match = 1;
2354 goto end_of_match;
2355 }
2356 break;
2357 }
2358 }
2359
2360 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002361 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002362 if (LIST_ISEMPTY(&expect->onerror_fmt))
2363 desc = htx_sl_res_reason(sl);
2364 break;
2365 }
2366
Christopher Faulet61cc8522020-04-20 14:54:42 +02002367 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002368 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002369 case TCPCHK_EXPECT_HTTP_BODY_LF:
2370 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002371 chunk_reset(&trash);
2372 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2373 enum htx_blk_type type = htx_get_blk_type(blk);
2374
2375 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2376 break;
2377 if (type == HTX_BLK_DATA) {
2378 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2379 break;
2380 }
2381 }
2382
2383 if (!b_data(&trash)) {
2384 if (!last_read)
2385 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002386 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002387 if (LIST_ISEMPTY(&expect->onerror_fmt))
2388 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002389 goto error;
2390 }
2391
Christopher Fauletaaab0832020-05-05 15:54:22 +02002392 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2393 tmp = alloc_trash_chunk();
2394 if (!tmp) {
2395 status = HCHK_STATUS_L7RSP;
2396 desc = ist("Failed to allocate buffer to eval log-format string");
2397 goto error;
2398 }
2399 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2400 if (!b_data(tmp)) {
2401 status = HCHK_STATUS_L7RSP;
2402 desc = ist("log-format string evaluated to an empty string");
2403 goto error;
2404 }
2405 }
2406
Christopher Faulet61cc8522020-04-20 14:54:42 +02002407 if (!last_read &&
2408 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002409 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002410 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2411 ret = TCPCHK_EVAL_WAIT;
2412 goto out;
2413 }
2414
2415 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002416 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002417 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2418 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002419 else
2420 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2421
2422 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002423 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002424 if (LIST_ISEMPTY(&expect->onerror_fmt))
2425 desc = (inverse
2426 ? ist("HTTP check matched unwanted content")
2427 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002428 break;
2429
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002430
Christopher Faulet61cc8522020-04-20 14:54:42 +02002431 default:
2432 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002433 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002434 goto error;
2435 }
2436
Christopher Faulet61cc8522020-04-20 14:54:42 +02002437 /* Wait for more data on mismatch only if no minimum is defined (-1),
2438 * otherwise the absence of match is already conclusive.
2439 */
2440 if (!match && !last_read && (expect->min_recv == -1)) {
2441 ret = TCPCHK_EVAL_WAIT;
2442 goto out;
2443 }
2444
2445 if (!(match ^ inverse))
2446 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002447
2448 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002449 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002450 free_trash_chunk(nbuf);
2451 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002452 free_trash_chunk(msg);
2453 return ret;
2454
2455 error:
2456 ret = TCPCHK_EVAL_STOP;
2457 msg = alloc_trash_chunk();
2458 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002459 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002460 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2461 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002462
2463 wait_more_data:
2464 ret = TCPCHK_EVAL_WAIT;
2465 goto out;
2466}
2467
Christopher Faulet61cc8522020-04-20 14:54:42 +02002468/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2469 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2470 * if an error occurred.
2471 */
2472static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2473{
2474 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2475 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002476 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002477 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002478 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002479 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002480
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002482
Christopher Faulet61cc8522020-04-20 14:54:42 +02002483 /* The current expect might need more data than the previous one, check again
2484 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002485 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002486 if (!last_read) {
2487 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2488 (b_data(&check->bi) < istlen(expect->data))) {
2489 ret = TCPCHK_EVAL_WAIT;
2490 goto out;
2491 }
2492 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2493 ret = TCPCHK_EVAL_WAIT;
2494 goto out;
2495 }
2496 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002497
Christopher Faulet61cc8522020-04-20 14:54:42 +02002498 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2499 /* Make GCC happy ; initialize match to a failure state. */
2500 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002501 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002502
Christopher Faulet61cc8522020-04-20 14:54:42 +02002503 switch (expect->type) {
2504 case TCPCHK_EXPECT_STRING:
2505 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002506 match = my_memmem(b_head(&check->bi), b_data(&check->bi), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002507 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002508 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002509 match = regex_exec2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002510 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002511
Christopher Faulet67a23452020-05-05 18:10:01 +02002512 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002513 chunk_reset(&trash);
2514 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002515 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002516 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002517
2518 case TCPCHK_EXPECT_STRING_LF:
2519 case TCPCHK_EXPECT_BINARY_LF:
2520 match = 0;
2521 tmp = alloc_trash_chunk();
2522 if (!tmp) {
2523 status = HCHK_STATUS_L7RSP;
2524 desc = ist("Failed to allocate buffer to eval format string");
2525 goto error;
2526 }
2527 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2528 if (!b_data(tmp)) {
2529 status = HCHK_STATUS_L7RSP;
2530 desc = ist("log-format string evaluated to an empty string");
2531 goto error;
2532 }
2533 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2534 int len = tmp->data;
2535 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2536 status = HCHK_STATUS_L7RSP;
2537 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2538 goto error;
2539 }
2540 tmp->data = len;
2541 }
2542 if (b_data(&check->bi) < tmp->data) {
2543 if (!last_read) {
2544 ret = TCPCHK_EVAL_WAIT;
2545 goto out;
2546 }
2547 break;
2548 }
2549 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2550 break;
2551
Christopher Faulet61cc8522020-04-20 14:54:42 +02002552 case TCPCHK_EXPECT_CUSTOM:
2553 if (expect->custom)
2554 ret = expect->custom(check, rule, last_read);
2555 goto out;
2556 default:
2557 /* Should never happen. */
2558 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002559 goto out;
2560 }
2561
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002562
Christopher Faulet61cc8522020-04-20 14:54:42 +02002563 /* Wait for more data on mismatch only if no minimum is defined (-1),
2564 * otherwise the absence of match is already conclusive.
2565 */
2566 if (!match && !last_read && (expect->min_recv == -1)) {
2567 ret = TCPCHK_EVAL_WAIT;
2568 goto out;
2569 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002570
Christopher Faulet61cc8522020-04-20 14:54:42 +02002571 /* Result as expected, next rule. */
2572 if (match ^ inverse)
2573 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002574
Christopher Fauletaaab0832020-05-05 15:54:22 +02002575 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002576 /* From this point on, we matched something we did not want, this is an error state. */
2577 ret = TCPCHK_EVAL_STOP;
2578 msg = alloc_trash_chunk();
2579 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002580 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002581 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002582 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002583
Christopher Faulet61cc8522020-04-20 14:54:42 +02002584 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002585 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002586 return ret;
2587}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002588
Christopher Faulet61cc8522020-04-20 14:54:42 +02002589/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002590 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002591 * waits.
2592 */
2593static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2594{
2595 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2596 struct act_rule *act_rule;
2597 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002598
Christopher Faulet61cc8522020-04-20 14:54:42 +02002599 act_rule =rule->action_kw.rule;
2600 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2601 if (act_ret != ACT_RET_CONT) {
2602 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2603 tcpcheck_get_step_id(check, rule));
2604 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2605 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002606 }
2607
Christopher Faulet61cc8522020-04-20 14:54:42 +02002608 return ret;
2609}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002610
Christopher Faulet61cc8522020-04-20 14:54:42 +02002611/* Executes a tcp-check ruleset. Note that this is called both from the
2612 * connection's wake() callback and from the check scheduling task. It returns
2613 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2614 * presenting the risk of an fd replacement.
2615 *
2616 * Please do NOT place any return statement in this function and only leave
2617 * via the out_end_tcpcheck label after setting retcode.
2618 */
2619static int tcpcheck_main(struct check *check)
2620{
2621 struct tcpcheck_rule *rule;
2622 struct conn_stream *cs = check->cs;
2623 struct connection *conn = cs_conn(cs);
2624 int must_read = 1, last_read = 0;
2625 int ret, retcode = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002626
Christopher Faulet61cc8522020-04-20 14:54:42 +02002627 /* here, we know that the check is complete or that it failed */
2628 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002629 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002630
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002631 /* Note: the conn-stream and the connection may only be undefined before
2632 * the first rule evaluation (it is always a connect rule) or when the
2633 * conn-stream allocation failed on the first connect.
2634 */
2635
Christopher Faulet61cc8522020-04-20 14:54:42 +02002636 /* 1- check for connection error, if any */
2637 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2638 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002639
Christopher Faulet61cc8522020-04-20 14:54:42 +02002640 /* 2- check if we are waiting for the connection establishment. It only
2641 * happens during TCPCHK_ACT_CONNECT. */
2642 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002643 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002644 struct tcpcheck_rule *next;
2645
2646 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2647 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002648 if (!(check->wait_list.events & SUB_RETRY_SEND))
2649 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2650 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002651 else {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002652 if (!(check->wait_list.events & SUB_RETRY_RECV))
2653 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2654 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002655 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002656 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002657 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002658 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002659
2660 /* 3- check for pending outgoing data. It only happens during
2661 * TCPCHK_ACT_SEND. */
2662 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002663 if (b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002664 /* We're already waiting to be able to send, give up */
2665 if (check->wait_list.events & SUB_RETRY_SEND)
2666 goto out;
2667
Christopher Faulet6d471212020-04-22 11:09:25 +02002668 ret = conn->mux->snd_buf(cs, &check->bo,
2669 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002670 if (ret <= 0) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002671 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002672 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002673 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002674 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002675 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002676 goto out;
2677 }
2678 }
2679 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002680 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002681
Christopher Faulet61cc8522020-04-20 14:54:42 +02002682 /* 4- check if a rule must be resume. It happens if check->current_step
2683 * is defined. */
2684 else if (check->current_step)
2685 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002686
Christopher Faulet61cc8522020-04-20 14:54:42 +02002687 /* 5- It is the first evaluation. We must create a session and preset
2688 * tcp-check variables */
2689 else {
2690 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002691
Christopher Faulet61cc8522020-04-20 14:54:42 +02002692 /* First evaluation, create a session */
2693 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2694 if (!check->sess) {
2695 chunk_printf(&trash, "TCPCHK error allocating check session");
2696 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2697 goto out_end_tcpcheck;
2698 }
2699 vars_init(&check->vars, SCOPE_CHECK);
2700 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002701
Christopher Faulet61cc8522020-04-20 14:54:42 +02002702 /* Preset tcp-check variables */
2703 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2704 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002705
Christopher Faulet61cc8522020-04-20 14:54:42 +02002706 memset(&smp, 0, sizeof(smp));
2707 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2708 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002709 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002710 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002711 }
2712
Christopher Faulet61cc8522020-04-20 14:54:42 +02002713 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002714
Christopher Faulet61cc8522020-04-20 14:54:42 +02002715 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
2716 enum tcpcheck_eval_ret eval_ret;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002717
Christopher Faulet61cc8522020-04-20 14:54:42 +02002718 check->code = 0;
2719 switch (rule->action) {
2720 case TCPCHK_ACT_CONNECT:
2721 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002722
Christopher Faulet61cc8522020-04-20 14:54:42 +02002723 /* close but not release yet previous connection */
2724 if (check->cs) {
2725 cs_close(check->cs);
2726 retcode = -1; /* do not reuse the fd in the caller! */
2727 }
2728 eval_ret = tcpcheck_eval_connect(check, rule);
Christopher Faulet3cbdd222020-05-26 11:14:50 +02002729
2730 /* Refresh conn-stream and connection */
2731 cs = check->cs;
2732 conn = cs_conn(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002733 must_read = 1; last_read = 0;
2734 break;
2735 case TCPCHK_ACT_SEND:
2736 check->current_step = rule;
2737 eval_ret = tcpcheck_eval_send(check, rule);
2738 must_read = 1;
2739 break;
2740 case TCPCHK_ACT_EXPECT:
2741 check->current_step = rule;
2742 if (must_read) {
2743 if (check->proxy->timeout.check)
2744 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002745
Christopher Faulet61cc8522020-04-20 14:54:42 +02002746 eval_ret = tcpcheck_eval_recv(check, rule);
2747 if (eval_ret == TCPCHK_EVAL_STOP)
2748 goto out_end_tcpcheck;
2749 else if (eval_ret == TCPCHK_EVAL_WAIT)
2750 goto out;
2751 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2752 must_read = 0;
2753 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002754
Christopher Faulet61cc8522020-04-20 14:54:42 +02002755 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2756 ? tcpcheck_eval_expect_http(check, rule, last_read)
2757 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002758
Christopher Faulet61cc8522020-04-20 14:54:42 +02002759 if (eval_ret == TCPCHK_EVAL_WAIT) {
2760 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002761 if (!(check->wait_list.events & SUB_RETRY_RECV))
2762 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002763 }
2764 break;
2765 case TCPCHK_ACT_ACTION_KW:
2766 /* Don't update the current step */
2767 eval_ret = tcpcheck_eval_action_kw(check, rule);
2768 break;
2769 default:
2770 /* Otherwise, just go to the next one and don't update
2771 * the current step
2772 */
2773 eval_ret = TCPCHK_EVAL_CONTINUE;
2774 break;
2775 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002776
Christopher Faulet61cc8522020-04-20 14:54:42 +02002777 switch (eval_ret) {
2778 case TCPCHK_EVAL_CONTINUE:
2779 break;
2780 case TCPCHK_EVAL_WAIT:
2781 goto out;
2782 case TCPCHK_EVAL_STOP:
2783 goto out_end_tcpcheck;
2784 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002785 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002786
Christopher Faulet61cc8522020-04-20 14:54:42 +02002787 /* All rules was evaluated */
2788 if (check->current_step) {
2789 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002790
Christopher Faulet61cc8522020-04-20 14:54:42 +02002791 if (rule->action == TCPCHK_ACT_EXPECT) {
2792 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002793 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002794
Christopher Faulet61cc8522020-04-20 14:54:42 +02002795 if (check->server &&
2796 (check->server->proxy->options & PR_O_DISABLE404) &&
2797 (check->server->next_state != SRV_ST_STOPPED) &&
2798 (check->code == 404)) {
2799 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2800 goto out_end_tcpcheck;
2801 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002802
Christopher Faulet61cc8522020-04-20 14:54:42 +02002803 msg = alloc_trash_chunk();
2804 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002805 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002806 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2807 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002808 free_trash_chunk(msg);
2809 }
2810 else if (rule->action == TCPCHK_ACT_CONNECT) {
2811 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002812 enum healthcheck_status status = HCHK_STATUS_L4OK;
2813#ifdef USE_OPENSSL
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002814 if (ssl_sock_is_ssl(conn))
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002815 status = HCHK_STATUS_L6OK;
2816#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002817 set_server_check_status(check, status, msg);
2818 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002819 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002820 else
2821 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002822
Christopher Faulet61cc8522020-04-20 14:54:42 +02002823 out_end_tcpcheck:
2824 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2825 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002826
Christopher Faulet61cc8522020-04-20 14:54:42 +02002827 out:
2828 return retcode;
2829}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002830
Christopher Faulet14cd3162020-04-16 14:50:06 +02002831
Christopher Faulet61cc8522020-04-20 14:54:42 +02002832/**************************************************************************/
2833/************** Health-checks based on an external process ****************/
2834/**************************************************************************/
2835static struct list pid_list = LIST_HEAD_INIT(pid_list);
2836static struct pool_head *pool_head_pid_list;
2837__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002838
Christopher Faulet61cc8522020-04-20 14:54:42 +02002839struct extcheck_env {
2840 char *name; /* environment variable name */
2841 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2842};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002843
Christopher Faulet61cc8522020-04-20 14:54:42 +02002844/* environment variables memory requirement for different types of data */
2845#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2846 * such environment variables are not updatable. */
2847#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2848#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2849#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002850
Christopher Faulet61cc8522020-04-20 14:54:42 +02002851/* external checks environment variables */
2852enum {
2853 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002854
Christopher Faulet61cc8522020-04-20 14:54:42 +02002855 /* Proxy specific environment variables */
2856 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2857 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2858 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2859 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002860
Christopher Faulet61cc8522020-04-20 14:54:42 +02002861 /* Server specific environment variables */
2862 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2863 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2864 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2865 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2866 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2867 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002868
Christopher Faulet61cc8522020-04-20 14:54:42 +02002869 EXTCHK_SIZE
2870};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002871
Christopher Faulet61cc8522020-04-20 14:54:42 +02002872const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2873 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2874 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2875 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2876 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2877 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2878 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2879 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2880 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2881 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2882 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2883 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2884};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002885
Christopher Faulet61cc8522020-04-20 14:54:42 +02002886void block_sigchld(void)
2887{
2888 sigset_t set;
2889 sigemptyset(&set);
2890 sigaddset(&set, SIGCHLD);
2891 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2892}
Willy Tarreaube373152018-09-06 11:45:30 +02002893
Christopher Faulet61cc8522020-04-20 14:54:42 +02002894void unblock_sigchld(void)
2895{
2896 sigset_t set;
2897 sigemptyset(&set);
2898 sigaddset(&set, SIGCHLD);
2899 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002900}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002901
Christopher Faulet61cc8522020-04-20 14:54:42 +02002902static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002903{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002904 struct pid_list *elem;
2905 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002906
Christopher Faulet61cc8522020-04-20 14:54:42 +02002907 elem = pool_alloc(pool_head_pid_list);
2908 if (!elem)
2909 return NULL;
2910 elem->pid = pid;
2911 elem->t = t;
2912 elem->exited = 0;
2913 check->curpid = elem;
2914 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002915
Christopher Faulet61cc8522020-04-20 14:54:42 +02002916 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2917 LIST_ADD(&pid_list, &elem->list);
2918 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002919
Christopher Faulet61cc8522020-04-20 14:54:42 +02002920 return elem;
2921}
Christopher Faulete5870d82020-04-15 11:32:03 +02002922
Christopher Faulet61cc8522020-04-20 14:54:42 +02002923static void pid_list_del(struct pid_list *elem)
2924{
2925 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002926
Christopher Faulet61cc8522020-04-20 14:54:42 +02002927 if (!elem)
2928 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002929
Christopher Faulet61cc8522020-04-20 14:54:42 +02002930 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2931 LIST_DEL(&elem->list);
2932 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002933
Christopher Faulet61cc8522020-04-20 14:54:42 +02002934 if (!elem->exited)
2935 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002936
Christopher Faulet61cc8522020-04-20 14:54:42 +02002937 check = elem->t->context;
2938 check->curpid = NULL;
2939 pool_free(pool_head_pid_list, elem);
2940}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002941
Christopher Faulet61cc8522020-04-20 14:54:42 +02002942/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2943static void pid_list_expire(pid_t pid, int status)
2944{
2945 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002946
Christopher Faulet61cc8522020-04-20 14:54:42 +02002947 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2948 list_for_each_entry(elem, &pid_list, list) {
2949 if (elem->pid == pid) {
2950 elem->t->expire = now_ms;
2951 elem->status = status;
2952 elem->exited = 1;
2953 task_wakeup(elem->t, TASK_WOKEN_IO);
2954 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002955 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002956 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002957 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2958}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002959
Christopher Faulet61cc8522020-04-20 14:54:42 +02002960static void sigchld_handler(struct sig_handler *sh)
2961{
2962 pid_t pid;
2963 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002964
Christopher Faulet61cc8522020-04-20 14:54:42 +02002965 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2966 pid_list_expire(pid, status);
2967}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002968
Christopher Faulet61cc8522020-04-20 14:54:42 +02002969static int init_pid_list(void)
2970{
2971 if (pool_head_pid_list != NULL)
2972 /* Nothing to do */
2973 return 0;
2974
2975 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2976 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2977 strerror(errno));
2978 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002979 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002980
Christopher Faulet61cc8522020-04-20 14:54:42 +02002981 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2982 if (pool_head_pid_list == NULL) {
2983 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2984 strerror(errno));
2985 return 1;
2986 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002987
Christopher Faulet61cc8522020-04-20 14:54:42 +02002988 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002989}
2990
Christopher Faulet61cc8522020-04-20 14:54:42 +02002991/* helper macro to set an environment variable and jump to a specific label on failure. */
2992#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002993
Christopher Faulet61cc8522020-04-20 14:54:42 +02002994/*
2995 * helper function to allocate enough memory to store an environment variable.
2996 * It will also check that the environment variable is updatable, and silently
2997 * fail if not.
2998 */
2999static int extchk_setenv(struct check *check, int idx, const char *value)
3000{
3001 int len, ret;
3002 char *envname;
3003 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003004
Christopher Faulet61cc8522020-04-20 14:54:42 +02003005 if (idx < 0 || idx >= EXTCHK_SIZE) {
3006 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
3007 return 1;
3008 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003009
Christopher Faulet61cc8522020-04-20 14:54:42 +02003010 envname = extcheck_envs[idx].name;
3011 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003012
Christopher Faulet61cc8522020-04-20 14:54:42 +02003013 /* Check if the environment variable is already set, and silently reject
3014 * the update if this one is not updatable. */
3015 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3016 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003017
Christopher Faulet61cc8522020-04-20 14:54:42 +02003018 /* Instead of sending NOT_USED, sending an empty value is preferable */
3019 if (strcmp(value, "NOT_USED") == 0) {
3020 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003021 }
3022
Christopher Faulet61cc8522020-04-20 14:54:42 +02003023 len = strlen(envname) + 1;
3024 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3025 len += strlen(value);
3026 else
3027 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003028
Christopher Faulet61cc8522020-04-20 14:54:42 +02003029 if (!check->envp[idx])
3030 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003031
Christopher Faulet61cc8522020-04-20 14:54:42 +02003032 if (!check->envp[idx]) {
3033 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3034 return 1;
3035 }
3036 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3037 if (ret < 0) {
3038 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3039 return 1;
3040 }
3041 else if (ret > len) {
3042 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3043 return 1;
3044 }
3045 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003046}
3047
Christopher Faulet61cc8522020-04-20 14:54:42 +02003048static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003049{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003050 struct server *s = check->server;
3051 struct proxy *px = s->proxy;
3052 struct listener *listener = NULL, *l;
3053 int i;
3054 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3055 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003056
Christopher Faulet61cc8522020-04-20 14:54:42 +02003057 list_for_each_entry(l, &px->conf.listeners, by_fe)
3058 /* Use the first INET, INET6 or UNIX listener */
3059 if (l->addr.ss_family == AF_INET ||
3060 l->addr.ss_family == AF_INET6 ||
3061 l->addr.ss_family == AF_UNIX) {
3062 listener = l;
3063 break;
3064 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003065
Christopher Faulet61cc8522020-04-20 14:54:42 +02003066 check->curpid = NULL;
3067 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3068 if (!check->envp) {
3069 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3070 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003071 }
3072
Christopher Faulet61cc8522020-04-20 14:54:42 +02003073 check->argv = calloc(6, sizeof(char *));
3074 if (!check->argv) {
3075 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3076 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003077 }
3078
Christopher Faulet61cc8522020-04-20 14:54:42 +02003079 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003080
Christopher Faulet61cc8522020-04-20 14:54:42 +02003081 if (!listener) {
3082 check->argv[1] = strdup("NOT_USED");
3083 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003084 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003085 else if (listener->addr.ss_family == AF_INET ||
3086 listener->addr.ss_family == AF_INET6) {
3087 addr_to_str(&listener->addr, buf, sizeof(buf));
3088 check->argv[1] = strdup(buf);
3089 port_to_str(&listener->addr, buf, sizeof(buf));
3090 check->argv[2] = strdup(buf);
3091 }
3092 else if (listener->addr.ss_family == AF_UNIX) {
3093 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003094
Christopher Faulet61cc8522020-04-20 14:54:42 +02003095 un = (struct sockaddr_un *)&listener->addr;
3096 check->argv[1] = strdup(un->sun_path);
3097 check->argv[2] = strdup("NOT_USED");
3098 }
3099 else {
3100 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3101 goto err;
3102 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003103
Christopher Faulet61cc8522020-04-20 14:54:42 +02003104 if (!check->argv[1] || !check->argv[2]) {
3105 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3106 goto err;
3107 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003108
Christopher Faulet61cc8522020-04-20 14:54:42 +02003109 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3110 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3111 if (!check->argv[3] || !check->argv[4]) {
3112 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3113 goto err;
3114 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003115
Christopher Faulet61cc8522020-04-20 14:54:42 +02003116 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3117 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3118 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003119
Christopher Faulet61cc8522020-04-20 14:54:42 +02003120 for (i = 0; i < 5; i++) {
3121 if (!check->argv[i]) {
3122 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3123 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003124 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003125 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003126
Christopher Faulet61cc8522020-04-20 14:54:42 +02003127 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3128 /* Add proxy environment variables */
3129 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3130 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3131 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3132 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3133 /* Add server environment variables */
3134 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3135 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3136 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3137 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3138 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3139 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003140
Christopher Faulet61cc8522020-04-20 14:54:42 +02003141 /* Ensure that we don't leave any hole in check->envp */
3142 for (i = 0; i < EXTCHK_SIZE; i++)
3143 if (!check->envp[i])
3144 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003145
Christopher Faulet61cc8522020-04-20 14:54:42 +02003146 return 1;
3147err:
3148 if (check->envp) {
3149 for (i = 0; i < EXTCHK_SIZE; i++)
3150 free(check->envp[i]);
3151 free(check->envp);
3152 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003153 }
3154
Christopher Faulet61cc8522020-04-20 14:54:42 +02003155 if (check->argv) {
3156 for (i = 1; i < 5; i++)
3157 free(check->argv[i]);
3158 free(check->argv);
3159 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003160 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003161 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003162}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003163
Christopher Faulet61cc8522020-04-20 14:54:42 +02003164/*
3165 * establish a server health-check that makes use of a process.
3166 *
3167 * It can return one of :
3168 * - SF_ERR_NONE if everything's OK
3169 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3170 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3171 *
3172 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003173 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003174static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003175{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003176 char buf[256];
3177 struct check *check = t->context;
3178 struct server *s = check->server;
3179 struct proxy *px = s->proxy;
3180 int status;
3181 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003182
Christopher Faulet61cc8522020-04-20 14:54:42 +02003183 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003184
Christopher Faulet61cc8522020-04-20 14:54:42 +02003185 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003186
Christopher Faulet61cc8522020-04-20 14:54:42 +02003187 pid = fork();
3188 if (pid < 0) {
3189 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3190 (global.tune.options & GTUNE_INSECURE_FORK) ?
3191 "" : " (likely caused by missing 'insecure-fork-wanted')",
3192 strerror(errno));
3193 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003194 goto out;
3195 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003196 if (pid == 0) {
3197 /* Child */
3198 extern char **environ;
3199 struct rlimit limit;
3200 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003201
Christopher Faulet61cc8522020-04-20 14:54:42 +02003202 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3203 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003204
Christopher Faulet61cc8522020-04-20 14:54:42 +02003205 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003206
Christopher Faulet61cc8522020-04-20 14:54:42 +02003207 /* restore the initial FD limits */
3208 limit.rlim_cur = rlim_fd_cur_at_boot;
3209 limit.rlim_max = rlim_fd_max_at_boot;
3210 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3211 getrlimit(RLIMIT_NOFILE, &limit);
3212 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3213 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3214 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3215 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003216
Christopher Faulet61cc8522020-04-20 14:54:42 +02003217 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003218
Christopher Faulet61cc8522020-04-20 14:54:42 +02003219 /* Update some environment variables and command args: curconn, server addr and server port */
3220 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003221
Christopher Faulet61cc8522020-04-20 14:54:42 +02003222 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3223 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003224
Christopher Faulet61cc8522020-04-20 14:54:42 +02003225 *check->argv[4] = 0;
3226 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3227 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3228 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003229
Christopher Faulet61cc8522020-04-20 14:54:42 +02003230 haproxy_unblock_signals();
3231 execvp(px->check_command, check->argv);
3232 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3233 strerror(errno));
3234 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003235 }
3236
Christopher Faulet61cc8522020-04-20 14:54:42 +02003237 /* Parent */
3238 if (check->result == CHK_RES_UNKNOWN) {
3239 if (pid_list_add(pid, t) != NULL) {
3240 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3241
3242 if (px->timeout.check && px->timeout.connect) {
3243 int t_con = tick_add(now_ms, px->timeout.connect);
3244 t->expire = tick_first(t->expire, t_con);
3245 }
3246 status = SF_ERR_NONE;
3247 goto out;
3248 }
3249 else {
3250 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3251 }
3252 kill(pid, SIGTERM); /* process creation error */
3253 }
3254 else
3255 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3256
3257out:
3258 unblock_sigchld();
3259 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003260}
3261
Christopher Faulet61cc8522020-04-20 14:54:42 +02003262/*
3263 * manages a server health-check that uses an external process. Returns
3264 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003265 *
3266 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003267 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003268 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003269static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003270{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003271 struct check *check = context;
3272 struct server *s = check->server;
3273 int rv;
3274 int ret;
3275 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003276
Christopher Faulet61cc8522020-04-20 14:54:42 +02003277 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3278 if (!(check->state & CHK_ST_INPROGRESS)) {
3279 /* no check currently running */
3280 if (!expired) /* woke up too early */
3281 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003282
Christopher Faulet61cc8522020-04-20 14:54:42 +02003283 /* we don't send any health-checks when the proxy is
3284 * stopped, the server should not be checked or the check
3285 * is disabled.
3286 */
3287 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3288 s->proxy->state == PR_STSTOPPED)
3289 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003290
Christopher Faulet61cc8522020-04-20 14:54:42 +02003291 /* we'll initiate a new check */
3292 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003293
Christopher Faulet61cc8522020-04-20 14:54:42 +02003294 check->state |= CHK_ST_INPROGRESS;
3295
3296 ret = connect_proc_chk(t);
3297 if (ret == SF_ERR_NONE) {
3298 /* the process was forked, we allow up to min(inter,
3299 * timeout.connect) for it to report its status, but
3300 * only when timeout.check is set as it may be to short
3301 * for a full check otherwise.
3302 */
3303 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3304
3305 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3306 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3307 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003308 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003309 task_set_affinity(t, tid_bit);
3310 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003311 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003312
Christopher Faulet61cc8522020-04-20 14:54:42 +02003313 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003314
Christopher Faulet61cc8522020-04-20 14:54:42 +02003315 check->state &= ~CHK_ST_INPROGRESS;
3316 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003317
Christopher Faulet61cc8522020-04-20 14:54:42 +02003318 /* we allow up to min(inter, timeout.connect) for a connection
3319 * to establish but only when timeout.check is set
3320 * as it may be to short for a full check otherwise
3321 */
3322 while (tick_is_expired(t->expire, now_ms)) {
3323 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003324
Christopher Faulet61cc8522020-04-20 14:54:42 +02003325 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3326 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003327
Christopher Faulet61cc8522020-04-20 14:54:42 +02003328 if (s->proxy->timeout.check)
3329 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003330 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003331 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003332 else {
3333 /* there was a test running.
3334 * First, let's check whether there was an uncaught error,
3335 * which can happen on connect timeout or error.
3336 */
3337 if (check->result == CHK_RES_UNKNOWN) {
3338 /* good connection is enough for pure TCP check */
3339 struct pid_list *elem = check->curpid;
3340 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003341
Christopher Faulet61cc8522020-04-20 14:54:42 +02003342 if (elem->exited) {
3343 status = elem->status; /* Save in case the process exits between use below */
3344 if (!WIFEXITED(status))
3345 check->code = -1;
3346 else
3347 check->code = WEXITSTATUS(status);
3348 if (!WIFEXITED(status) || WEXITSTATUS(status))
3349 status = HCHK_STATUS_PROCERR;
3350 else
3351 status = HCHK_STATUS_PROCOK;
3352 } else if (expired) {
3353 status = HCHK_STATUS_PROCTOUT;
3354 ha_warning("kill %d\n", (int)elem->pid);
3355 kill(elem->pid, SIGTERM);
3356 }
3357 set_server_check_status(check, status, NULL);
3358 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003359
Christopher Faulet61cc8522020-04-20 14:54:42 +02003360 if (check->result == CHK_RES_FAILED) {
3361 /* a failure or timeout detected */
3362 check_notify_failure(check);
3363 }
3364 else if (check->result == CHK_RES_CONDPASS) {
3365 /* check is OK but asks for stopping mode */
3366 check_notify_stopping(check);
3367 }
3368 else if (check->result == CHK_RES_PASSED) {
3369 /* a success was detected */
3370 check_notify_success(check);
3371 }
3372 task_set_affinity(t, 1);
3373 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003374
Christopher Faulet61cc8522020-04-20 14:54:42 +02003375 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003376
Christopher Faulet61cc8522020-04-20 14:54:42 +02003377 rv = 0;
3378 if (global.spread_checks > 0) {
3379 rv = srv_getinter(check) * global.spread_checks / 100;
3380 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3381 }
3382 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3383 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003384
Christopher Faulet61cc8522020-04-20 14:54:42 +02003385 reschedule:
3386 while (tick_is_expired(t->expire, now_ms))
3387 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003388
Christopher Faulet61cc8522020-04-20 14:54:42 +02003389 out_unlock:
3390 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3391 return t;
3392}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003393
Baptiste Assmann248f1172018-03-01 21:49:01 +01003394
Christopher Faulet61cc8522020-04-20 14:54:42 +02003395/**************************************************************************/
3396/***************** Health-checks based on connections *********************/
3397/**************************************************************************/
3398/* This function is used only for server health-checks. It handles connection
3399 * status updates including errors. If necessary, it wakes the check task up.
3400 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3401 * connection (eg: reconnect). It relies on tcpcheck_main().
3402 */
3403static int wake_srv_chk(struct conn_stream *cs)
3404{
3405 struct connection *conn = cs->conn;
3406 struct check *check = cs->data;
3407 struct email_alertq *q = container_of(check, typeof(*q), check);
3408 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003409
Christopher Faulet61cc8522020-04-20 14:54:42 +02003410 if (check->server)
3411 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3412 else
3413 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003414
Christopher Faulet61cc8522020-04-20 14:54:42 +02003415 /* we may have to make progress on the TCP checks */
3416 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003417
Christopher Faulet61cc8522020-04-20 14:54:42 +02003418 cs = check->cs;
3419 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003420
Christopher Faulet61cc8522020-04-20 14:54:42 +02003421 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3422 /* We may get error reports bypassing the I/O handlers, typically
3423 * the case when sending a pure TCP check which fails, then the I/O
3424 * handlers above are not called. This is completely handled by the
3425 * main processing task so let's simply wake it up. If we get here,
3426 * we expect errno to still be valid.
3427 */
3428 chk_report_conn_err(check, errno, 0);
3429 task_wakeup(check->task, TASK_WOKEN_IO);
3430 }
3431
3432 if (check->result != CHK_RES_UNKNOWN) {
3433 /* Check complete or aborted. If connection not yet closed do it
3434 * now and wake the check task up to be sure the result is
3435 * handled ASAP. */
3436 conn_sock_drain(conn);
3437 cs_close(cs);
3438 ret = -1;
3439 /* We may have been scheduled to run, and the
3440 * I/O handler expects to have a cs, so remove
3441 * the tasklet
3442 */
3443 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3444 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003445 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003446
3447 if (check->server)
3448 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003449 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003450 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003451
Christopher Faulet61cc8522020-04-20 14:54:42 +02003452 /* if a connection got replaced, we must absolutely prevent the connection
3453 * handler from touching its fd, and perform the FD polling updates ourselves
3454 */
3455 if (ret < 0)
3456 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003457
Christopher Faulet61cc8522020-04-20 14:54:42 +02003458 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003459}
3460
Christopher Faulet61cc8522020-04-20 14:54:42 +02003461/* This function checks if any I/O is wanted, and if so, attempts to do so */
3462static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003463{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003464 struct check *check = ctx;
3465 struct conn_stream *cs = check->cs;
3466 struct email_alertq *q = container_of(check, typeof(*q), check);
3467 int ret = 0;
Simon Hormanb1900d52015-01-30 11:22:54 +09003468
Christopher Faulet61cc8522020-04-20 14:54:42 +02003469 if (!(check->wait_list.events & SUB_RETRY_SEND))
3470 ret = wake_srv_chk(cs);
3471 if (ret == 0 && !(check->wait_list.events & SUB_RETRY_RECV)) {
3472 if (check->server)
3473 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3474 else
3475 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Hormanb1900d52015-01-30 11:22:54 +09003476
Christopher Faulet61cc8522020-04-20 14:54:42 +02003477 if (unlikely(check->result == CHK_RES_FAILED)) {
3478 /* collect possible new errors */
3479 if (cs->conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
3480 chk_report_conn_err(check, 0, 0);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003481
Christopher Faulet61cc8522020-04-20 14:54:42 +02003482 /* Reset the check buffer... */
3483 b_reset(&check->bi);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003484
Christopher Faulet61cc8522020-04-20 14:54:42 +02003485 /* Close the connection... We still attempt to nicely close if,
3486 * for instance, SSL needs to send a "close notify." Later, we perform
3487 * a hard close and reset the connection if some data are pending,
3488 * otherwise we end up with many TIME_WAITs and eat all the source port
3489 * range quickly. To avoid sending RSTs all the time, we first try to
3490 * drain pending data.
3491 */
3492 /* Call cs_shutr() first, to add the CO_FL_SOCK_RD_SH flag on the
3493 * connection, to make sure cs_shutw() will not lead to a shutdown()
3494 * that would provoke TIME_WAITs.
3495 */
3496 cs_shutr(cs, CS_SHR_DRAIN);
3497 cs_shutw(cs, CS_SHW_NORMAL);
Simon Hormanb1900d52015-01-30 11:22:54 +09003498
Christopher Faulet61cc8522020-04-20 14:54:42 +02003499 /* OK, let's not stay here forever */
3500 if (check->result == CHK_RES_FAILED)
3501 cs->conn->flags |= CO_FL_ERROR;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01003502
Christopher Faulet61cc8522020-04-20 14:54:42 +02003503 task_wakeup(t, TASK_WOKEN_IO);
3504 }
3505
3506 if (check->server)
3507 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3508 else
3509 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet23d86d12018-01-25 11:36:35 +01003510 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003511 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003512}
3513
Christopher Faulet61cc8522020-04-20 14:54:42 +02003514/* manages a server health-check that uses a connection. Returns
3515 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3516 *
3517 * Please do NOT place any return statement in this function and only leave
3518 * via the out_unlock label.
3519 */
3520static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003521{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003522 struct check *check = context;
3523 struct proxy *proxy = check->proxy;
3524 struct conn_stream *cs = check->cs;
3525 struct connection *conn = cs_conn(cs);
3526 int rv;
3527 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003528
Christopher Faulet61cc8522020-04-20 14:54:42 +02003529 if (check->server)
3530 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3531 if (!(check->state & CHK_ST_INPROGRESS)) {
3532 /* no check currently running */
3533 if (!expired) /* woke up too early */
3534 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003535
Christopher Faulet61cc8522020-04-20 14:54:42 +02003536 /* we don't send any health-checks when the proxy is
3537 * stopped, the server should not be checked or the check
3538 * is disabled.
3539 */
3540 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3541 proxy->state == PR_STSTOPPED)
3542 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003543
Christopher Faulet61cc8522020-04-20 14:54:42 +02003544 /* we'll initiate a new check */
3545 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003546
Christopher Faulet61cc8522020-04-20 14:54:42 +02003547 check->state |= CHK_ST_INPROGRESS;
3548 b_reset(&check->bi);
3549 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003550
Christopher Faulet61cc8522020-04-20 14:54:42 +02003551 task_set_affinity(t, tid_bit);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003552
Christopher Faulet99ff1052020-05-25 07:32:01 +02003553 check->current_step = NULL;
3554 tcpcheck_main(check);
3555 goto out_unlock;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003556 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003557 else {
3558 /* there was a test running.
3559 * First, let's check whether there was an uncaught error,
3560 * which can happen on connect timeout or error.
3561 */
3562 if (check->result == CHK_RES_UNKNOWN) {
3563 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3564 chk_report_conn_err(check, 0, expired);
3565 }
3566 else
3567 goto out_unlock; /* timeout not reached, wait again */
3568 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003569
Christopher Faulet61cc8522020-04-20 14:54:42 +02003570 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003571
Christopher Faulet61cc8522020-04-20 14:54:42 +02003572 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003573
Christopher Faulet61cc8522020-04-20 14:54:42 +02003574 if (conn && conn->xprt) {
3575 /* The check was aborted and the connection was not yet closed.
3576 * This can happen upon timeout, or when an external event such
3577 * as a failed response coupled with "observe layer7" caused the
3578 * server state to be suddenly changed.
3579 */
3580 conn_sock_drain(conn);
3581 cs_close(cs);
3582 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003583
Christopher Faulet61cc8522020-04-20 14:54:42 +02003584 if (cs) {
3585 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003586 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003587 /* We may have been scheduled to run, and the
3588 * I/O handler expects to have a cs, so remove
3589 * the tasklet
3590 */
3591 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3592 cs_destroy(cs);
3593 cs = check->cs = NULL;
3594 conn = NULL;
3595 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003596
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003597 if (check->sess != NULL) {
3598 vars_prune(&check->vars, check->sess, NULL);
3599 session_free(check->sess);
3600 check->sess = NULL;
3601 }
3602
Christopher Faulet61cc8522020-04-20 14:54:42 +02003603 if (check->server) {
3604 if (check->result == CHK_RES_FAILED) {
3605 /* a failure or timeout detected */
3606 check_notify_failure(check);
3607 }
3608 else if (check->result == CHK_RES_CONDPASS) {
3609 /* check is OK but asks for stopping mode */
3610 check_notify_stopping(check);
3611 }
3612 else if (check->result == CHK_RES_PASSED) {
3613 /* a success was detected */
3614 check_notify_success(check);
3615 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003616 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003617 task_set_affinity(t, MAX_THREADS_MASK);
3618 check->state &= ~CHK_ST_INPROGRESS;
3619
3620 if (check->server) {
3621 rv = 0;
3622 if (global.spread_checks > 0) {
3623 rv = srv_getinter(check) * global.spread_checks / 100;
3624 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3625 }
3626 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003627 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003628 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003629
Christopher Faulet61cc8522020-04-20 14:54:42 +02003630 reschedule:
3631 while (tick_is_expired(t->expire, now_ms))
3632 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3633 out_unlock:
3634 if (check->server)
3635 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3636 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003637}
3638
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003639
Christopher Faulet61cc8522020-04-20 14:54:42 +02003640/**************************************************************************/
3641/******************* Internals to parse tcp-check rules *******************/
3642/**************************************************************************/
3643struct action_kw_list tcp_check_keywords = {
3644 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3645};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003646
Christopher Faulet61cc8522020-04-20 14:54:42 +02003647/* Return the struct action_kw associated to a keyword */
3648static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003649{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003650 return action_lookup(&tcp_check_keywords.list, kw);
3651}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003652
Christopher Faulet61cc8522020-04-20 14:54:42 +02003653static void action_kw_tcp_check_build_list(struct buffer *chk)
3654{
3655 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003656}
3657
Christopher Faulet61cc8522020-04-20 14:54:42 +02003658/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3659 * returned on error.
3660 */
3661static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3662 struct list *rules, struct action_kw *kw,
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 act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003667
Christopher Faulet61cc8522020-04-20 14:54:42 +02003668 actrule = calloc(1, sizeof(*actrule));
3669 if (!actrule) {
3670 memprintf(errmsg, "out of memory");
3671 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003672 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003673 actrule->kw = kw;
3674 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003675
Christopher Faulet61cc8522020-04-20 14:54:42 +02003676 cur_arg++;
3677 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3678 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3679 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003680 }
3681
Christopher Faulet61cc8522020-04-20 14:54:42 +02003682 chk = calloc(1, sizeof(*chk));
3683 if (!chk) {
3684 memprintf(errmsg, "out of memory");
3685 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003686 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003687 chk->action = TCPCHK_ACT_ACTION_KW;
3688 chk->action_kw.rule = actrule;
3689 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003690
3691 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003692 free(actrule);
3693 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003694}
3695
Christopher Faulet61cc8522020-04-20 14:54:42 +02003696/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3697 * returned on error.
3698 */
3699static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3700 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003701{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003702 struct tcpcheck_rule *chk = NULL;
3703 struct sockaddr_storage *sk = NULL;
3704 char *comment = NULL, *sni = NULL, *alpn = NULL;
3705 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003706 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003707 unsigned short conn_opts = 0;
3708 long port = 0;
3709 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003710
Christopher Faulet61cc8522020-04-20 14:54:42 +02003711 list_for_each_entry(chk, rules, list) {
3712 if (chk->action == TCPCHK_ACT_CONNECT)
3713 break;
3714 if (chk->action == TCPCHK_ACT_COMMENT ||
3715 chk->action == TCPCHK_ACT_ACTION_KW ||
3716 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3717 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003718
Christopher Faulet61cc8522020-04-20 14:54:42 +02003719 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003720 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003721 "when there is a 'connect' step in the tcp-check ruleset");
3722 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003723 }
3724
Christopher Faulet61cc8522020-04-20 14:54:42 +02003725 cur_arg++;
3726 while (*(args[cur_arg])) {
3727 if (strcmp(args[cur_arg], "default") == 0)
3728 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3729 else if (strcmp(args[cur_arg], "addr") == 0) {
3730 int port1, port2;
3731 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003732
Christopher Faulet61cc8522020-04-20 14:54:42 +02003733 if (!*(args[cur_arg+1])) {
3734 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3735 goto error;
3736 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003737
Christopher Faulet61cc8522020-04-20 14:54:42 +02003738 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3739 if (!sk) {
3740 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3741 goto error;
3742 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003743
Christopher Faulet61cc8522020-04-20 14:54:42 +02003744 proto = protocol_by_family(sk->ss_family);
3745 if (!proto || !proto->connect) {
3746 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3747 args[cur_arg]);
3748 goto error;
3749 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003750
Christopher Faulet61cc8522020-04-20 14:54:42 +02003751 if (port1 != port2) {
3752 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3753 args[cur_arg], args[cur_arg+1]);
3754 goto error;
3755 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003756
Christopher Faulet61cc8522020-04-20 14:54:42 +02003757 cur_arg++;
3758 }
3759 else if (strcmp(args[cur_arg], "port") == 0) {
3760 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003761
Christopher Faulet61cc8522020-04-20 14:54:42 +02003762 if (!*(args[cur_arg+1])) {
3763 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3764 goto error;
3765 }
3766 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003767
Christopher Faulet61cc8522020-04-20 14:54:42 +02003768 port = 0;
3769 release_sample_expr(port_expr);
3770 p = args[cur_arg]; end = p + strlen(p);
3771 port = read_uint(&p, end);
3772 if (p != end) {
3773 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003774
Christopher Faulet61cc8522020-04-20 14:54:42 +02003775 px->conf.args.ctx = ARGC_SRV;
3776 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3777 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003778
Christopher Faulet61cc8522020-04-20 14:54:42 +02003779 if (!port_expr) {
3780 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3781 goto error;
3782 }
3783 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3784 memprintf(errmsg, "error detected while parsing port expression : "
3785 " fetch method '%s' extracts information from '%s', "
3786 "none of which is available here.\n",
3787 args[cur_arg], sample_src_names(port_expr->fetch->use));
3788 goto error;
3789 }
3790 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3791 }
3792 else if (port > 65535 || port < 1) {
3793 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3794 args[cur_arg]);
3795 goto error;
3796 }
3797 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003798 else if (strcmp(args[cur_arg], "proto") == 0) {
3799 if (!*(args[cur_arg+1])) {
3800 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3801 goto error;
3802 }
3803 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3804 if (!mux_proto) {
3805 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3806 goto error;
3807 }
3808 cur_arg++;
3809 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003810 else if (strcmp(args[cur_arg], "comment") == 0) {
3811 if (!*(args[cur_arg+1])) {
3812 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3813 goto error;
3814 }
3815 cur_arg++;
3816 free(comment);
3817 comment = strdup(args[cur_arg]);
3818 if (!comment) {
3819 memprintf(errmsg, "out of memory");
3820 goto error;
3821 }
3822 }
3823 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3824 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3825 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3826 conn_opts |= TCPCHK_OPT_SOCKS4;
3827 else if (strcmp(args[cur_arg], "linger") == 0)
3828 conn_opts |= TCPCHK_OPT_LINGER;
3829#ifdef USE_OPENSSL
3830 else if (strcmp(args[cur_arg], "ssl") == 0) {
3831 px->options |= PR_O_TCPCHK_SSL;
3832 conn_opts |= TCPCHK_OPT_SSL;
3833 }
3834 else if (strcmp(args[cur_arg], "sni") == 0) {
3835 if (!*(args[cur_arg+1])) {
3836 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3837 goto error;
3838 }
3839 cur_arg++;
3840 free(sni);
3841 sni = strdup(args[cur_arg]);
3842 if (!sni) {
3843 memprintf(errmsg, "out of memory");
3844 goto error;
3845 }
3846 }
3847 else if (strcmp(args[cur_arg], "alpn") == 0) {
3848#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3849 free(alpn);
3850 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3851 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3852 goto error;
3853 }
3854 cur_arg++;
3855#else
3856 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003857 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003858#endif
3859 }
3860#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003861
Christopher Faulet61cc8522020-04-20 14:54:42 +02003862 else {
3863 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3864#ifdef USE_OPENSSL
3865 ", 'ssl', 'sni', 'alpn'"
3866#endif /* USE_OPENSSL */
3867 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3868 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003869 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003870 }
3871 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003872 }
3873
Christopher Faulet61cc8522020-04-20 14:54:42 +02003874 chk = calloc(1, sizeof(*chk));
3875 if (!chk) {
3876 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003877 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003878 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003879 chk->action = TCPCHK_ACT_CONNECT;
3880 chk->comment = comment;
3881 chk->connect.port = port;
3882 chk->connect.options = conn_opts;
3883 chk->connect.sni = sni;
3884 chk->connect.alpn = alpn;
3885 chk->connect.alpn_len= alpn_len;
3886 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003887 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003888 if (sk)
3889 chk->connect.addr = *sk;
3890 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003891
Christopher Faulet61cc8522020-04-20 14:54:42 +02003892 error:
3893 free(alpn);
3894 free(sni);
3895 free(comment);
3896 release_sample_expr(port_expr);
3897 return NULL;
3898}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003899
Christopher Faulet61cc8522020-04-20 14:54:42 +02003900/* Parses and creates a tcp-check send rule. NULL is returned on error */
3901static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3902 const char *file, int line, char **errmsg)
3903{
3904 struct tcpcheck_rule *chk = NULL;
3905 char *comment = NULL, *data = NULL;
3906 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003907
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003908 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3909 type = TCPCHK_SEND_BINARY_LF;
3910 else if (strcmp(args[cur_arg], "send-binary") == 0)
3911 type = TCPCHK_SEND_BINARY;
3912 else if (strcmp(args[cur_arg], "send-lf") == 0)
3913 type = TCPCHK_SEND_STRING_LF;
3914 else if (strcmp(args[cur_arg], "send") == 0)
3915 type = TCPCHK_SEND_STRING;
3916
Christopher Faulet61cc8522020-04-20 14:54:42 +02003917 if (!*(args[cur_arg+1])) {
3918 memprintf(errmsg, "'%s' expects a %s as argument",
3919 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003920 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003921 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003922
Christopher Faulet61cc8522020-04-20 14:54:42 +02003923 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003924
Christopher Faulet61cc8522020-04-20 14:54:42 +02003925 cur_arg += 2;
3926 while (*(args[cur_arg])) {
3927 if (strcmp(args[cur_arg], "comment") == 0) {
3928 if (!*(args[cur_arg+1])) {
3929 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3930 goto error;
3931 }
3932 cur_arg++;
3933 free(comment);
3934 comment = strdup(args[cur_arg]);
3935 if (!comment) {
3936 memprintf(errmsg, "out of memory");
3937 goto error;
3938 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003939 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003940 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003941 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003942 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003943 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003944 }
3945 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003946 }
3947
Christopher Faulet61cc8522020-04-20 14:54:42 +02003948 chk = calloc(1, sizeof(*chk));
3949 if (!chk) {
3950 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003951 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003952 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003953 chk->action = TCPCHK_ACT_SEND;
3954 chk->comment = comment;
3955 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003956
Christopher Faulet61cc8522020-04-20 14:54:42 +02003957 switch (chk->send.type) {
3958 case TCPCHK_SEND_STRING:
3959 chk->send.data = ist2(strdup(data), strlen(data));
3960 if (!isttest(chk->send.data)) {
3961 memprintf(errmsg, "out of memory");
3962 goto error;
3963 }
3964 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003965 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003966 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003967 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003968 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3969 goto error;
3970 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003971 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003972 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003973 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003974 case TCPCHK_SEND_STRING_LF:
3975 case TCPCHK_SEND_BINARY_LF:
3976 LIST_INIT(&chk->send.fmt);
3977 px->conf.args.ctx = ARGC_SRV;
3978 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3979 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3980 goto error;
3981 }
3982 break;
3983 case TCPCHK_SEND_HTTP:
3984 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003985 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003986 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003987
Christopher Faulet61cc8522020-04-20 14:54:42 +02003988 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003989
Christopher Faulet61cc8522020-04-20 14:54:42 +02003990 error:
3991 free(chk);
3992 free(comment);
3993 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003994}
3995
Christopher Faulet61cc8522020-04-20 14:54:42 +02003996/* Parses and creates a http-check send rule. NULL is returned on error */
3997static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3998 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003999{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004000 struct tcpcheck_rule *chk = NULL;
4001 struct tcpcheck_http_hdr *hdr = NULL;
4002 struct http_hdr hdrs[global.tune.max_http_hdr];
4003 char *meth = NULL, *uri = NULL, *vsn = NULL;
4004 char *body = NULL, *comment = NULL;
4005 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004006 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004007
4008 cur_arg++;
4009 while (*(args[cur_arg])) {
4010 if (strcmp(args[cur_arg], "meth") == 0) {
4011 if (!*(args[cur_arg+1])) {
4012 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4013 goto error;
4014 }
4015 cur_arg++;
4016 meth = args[cur_arg];
4017 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004018 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004019 if (!*(args[cur_arg+1])) {
4020 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4021 goto error;
4022 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004023 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4024 if (strcmp(args[cur_arg], "uri-lf") == 0)
4025 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004026 cur_arg++;
4027 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004028 }
Christopher Faulet907701b2020-04-28 09:37:00 +02004029 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004030 if (!*(args[cur_arg+1])) {
4031 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4032 goto error;
4033 }
4034 cur_arg++;
4035 vsn = args[cur_arg];
4036 }
4037 else if (strcmp(args[cur_arg], "hdr") == 0) {
4038 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4039 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4040 goto error;
4041 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004042
4043 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4044 if (host_hdr >= 0) {
4045 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4046 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4047 goto error;
4048 }
4049 host_hdr = i;
4050 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004051 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4052 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4053 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4054 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004055
Christopher Faulet61cc8522020-04-20 14:54:42 +02004056 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4057 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4058 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004059 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004060 cur_arg += 2;
4061 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004062 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004063 if (!*(args[cur_arg+1])) {
4064 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4065 goto error;
4066 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004067 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4068 if (strcmp(args[cur_arg], "body-lf") == 0)
4069 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004070 cur_arg++;
4071 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004072 }
4073 else if (strcmp(args[cur_arg], "comment") == 0) {
4074 if (!*(args[cur_arg+1])) {
4075 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4076 goto error;
4077 }
4078 cur_arg++;
4079 free(comment);
4080 comment = strdup(args[cur_arg]);
4081 if (!comment) {
4082 memprintf(errmsg, "out of memory");
4083 goto error;
4084 }
4085 }
4086 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004087 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004088 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004089 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004090 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004091 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004092 }
4093
Christopher Faulet61cc8522020-04-20 14:54:42 +02004094 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004095
Christopher Faulet61cc8522020-04-20 14:54:42 +02004096 chk = calloc(1, sizeof(*chk));
4097 if (!chk) {
4098 memprintf(errmsg, "out of memory");
4099 goto error;
4100 }
4101 chk->action = TCPCHK_ACT_SEND;
4102 chk->comment = comment; comment = NULL;
4103 chk->send.type = TCPCHK_SEND_HTTP;
4104 chk->send.http.flags = flags;
4105 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004106
Christopher Faulet61cc8522020-04-20 14:54:42 +02004107 if (meth) {
4108 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4109 chk->send.http.meth.str.area = strdup(meth);
4110 chk->send.http.meth.str.data = strlen(meth);
4111 if (!chk->send.http.meth.str.area) {
4112 memprintf(errmsg, "out of memory");
4113 goto error;
4114 }
4115 }
4116 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004117 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4118 LIST_INIT(&chk->send.http.uri_fmt);
4119 px->conf.args.ctx = ARGC_SRV;
4120 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4121 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4122 goto error;
4123 }
4124 }
4125 else {
4126 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4127 if (!isttest(chk->send.http.uri)) {
4128 memprintf(errmsg, "out of memory");
4129 goto error;
4130 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004131 }
4132 }
4133 if (vsn) {
4134 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4135 if (!isttest(chk->send.http.vsn)) {
4136 memprintf(errmsg, "out of memory");
4137 goto error;
4138 }
4139 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004140 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004141 hdr = calloc(1, sizeof(*hdr));
4142 if (!hdr) {
4143 memprintf(errmsg, "out of memory");
4144 goto error;
4145 }
4146 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004147 hdr->name = istdup(hdrs[i].n);
4148 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004149 memprintf(errmsg, "out of memory");
4150 goto error;
4151 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004152
Christopher Fauletb61caf42020-04-21 10:57:42 +02004153 ist0(hdrs[i].v);
4154 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 +02004155 goto error;
4156 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4157 hdr = NULL;
4158 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004159
Christopher Faulet61cc8522020-04-20 14:54:42 +02004160 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004161 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4162 LIST_INIT(&chk->send.http.body_fmt);
4163 px->conf.args.ctx = ARGC_SRV;
4164 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4165 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4166 goto error;
4167 }
4168 }
4169 else {
4170 chk->send.http.body = ist2(strdup(body), strlen(body));
4171 if (!isttest(chk->send.http.body)) {
4172 memprintf(errmsg, "out of memory");
4173 goto error;
4174 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004175 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004176 }
4177
Christopher Faulet61cc8522020-04-20 14:54:42 +02004178 return chk;
4179
4180 error:
4181 free_tcpcheck_http_hdr(hdr);
4182 free_tcpcheck(chk, 0);
4183 free(comment);
4184 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004185}
4186
Christopher Faulet61cc8522020-04-20 14:54:42 +02004187/* Parses and creates a http-check comment rule. NULL is returned on error */
4188static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4189 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004190{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004191 struct tcpcheck_rule *chk = NULL;
4192 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004193
Christopher Faulet61cc8522020-04-20 14:54:42 +02004194 if (!*(args[cur_arg+1])) {
4195 memprintf(errmsg, "expects a string as argument");
4196 goto error;
4197 }
4198 cur_arg++;
4199 comment = strdup(args[cur_arg]);
4200 if (!comment) {
4201 memprintf(errmsg, "out of memory");
4202 goto error;
4203 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004204
Christopher Faulet61cc8522020-04-20 14:54:42 +02004205 chk = calloc(1, sizeof(*chk));
4206 if (!chk) {
4207 memprintf(errmsg, "out of memory");
4208 goto error;
4209 }
4210 chk->action = TCPCHK_ACT_COMMENT;
4211 chk->comment = comment;
4212 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004213
Christopher Faulet61cc8522020-04-20 14:54:42 +02004214 error:
4215 free(comment);
4216 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004217}
4218
Christopher Faulet61cc8522020-04-20 14:54:42 +02004219/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4220 * on error. <proto> is set to the right protocol flags (covered by the
4221 * TCPCHK_RULES_PROTO_CHK mask).
4222 */
4223static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4224 struct list *rules, unsigned int proto,
4225 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004226{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004227 struct tcpcheck_rule *prev_check, *chk = NULL;
4228 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004229 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004230 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004231 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4232 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4233 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004234 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004235 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004236 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004237
Christopher Faulet39708192020-05-05 10:47:36 +02004238 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004239 if (!*(args[cur_arg+1])) {
4240 memprintf(errmsg, "expects at least a matching pattern as arguments");
4241 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004242 }
4243
Christopher Faulet61cc8522020-04-20 14:54:42 +02004244 cur_arg++;
4245 while (*(args[cur_arg])) {
4246 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004247
Christopher Faulet61cc8522020-04-20 14:54:42 +02004248 rescan:
4249 if (strcmp(args[cur_arg], "min-recv") == 0) {
4250 if (in_pattern) {
4251 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4252 goto error;
4253 }
4254 if (!*(args[cur_arg+1])) {
4255 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4256 goto error;
4257 }
4258 /* Use an signed integer here because of chksize */
4259 cur_arg++;
4260 min_recv = atol(args[cur_arg]);
4261 if (min_recv < -1 || min_recv > INT_MAX) {
4262 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4263 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004264 }
4265 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004266 else if (*(args[cur_arg]) == '!') {
4267 in_pattern = 1;
4268 while (*(args[cur_arg]) == '!') {
4269 inverse = !inverse;
4270 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004271 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004272 if (!*(args[cur_arg]))
4273 cur_arg++;
4274 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004275 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004276 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4277 if (type != TCPCHK_EXPECT_UNDEF) {
4278 memprintf(errmsg, "only on pattern expected");
4279 goto error;
4280 }
4281 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004282 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004283 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004284 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004285
Christopher Faulet61cc8522020-04-20 14:54:42 +02004286 if (!*(args[cur_arg+1])) {
4287 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4288 goto error;
4289 }
4290 cur_arg++;
4291 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004292 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004293 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4294 if (proto == TCPCHK_RULES_HTTP_CHK)
4295 goto bad_http_kw;
4296 if (type != TCPCHK_EXPECT_UNDEF) {
4297 memprintf(errmsg, "only on pattern expected");
4298 goto error;
4299 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004300 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004301
Christopher Faulet61cc8522020-04-20 14:54:42 +02004302 if (!*(args[cur_arg+1])) {
4303 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4304 goto error;
4305 }
4306 cur_arg++;
4307 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004308 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004309 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4310 if (type != TCPCHK_EXPECT_UNDEF) {
4311 memprintf(errmsg, "only on pattern expected");
4312 goto error;
4313 }
4314 if (proto != TCPCHK_RULES_HTTP_CHK)
4315 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4316 else {
4317 if (*(args[cur_arg]) != 's')
4318 goto bad_http_kw;
4319 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4320 }
4321
4322 if (!*(args[cur_arg+1])) {
4323 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4324 goto error;
4325 }
4326 cur_arg++;
4327 pattern = args[cur_arg];
4328 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004329 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4330 if (proto != TCPCHK_RULES_HTTP_CHK)
4331 goto bad_tcp_kw;
4332 if (type != TCPCHK_EXPECT_UNDEF) {
4333 memprintf(errmsg, "only on pattern expected");
4334 goto error;
4335 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004336 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004337
Christopher Faulet61cc8522020-04-20 14:54:42 +02004338 if (!*(args[cur_arg+1])) {
4339 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4340 goto error;
4341 }
4342 cur_arg++;
4343 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004344 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004345 else if (strcmp(args[cur_arg], "custom") == 0) {
4346 if (in_pattern) {
4347 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4348 goto error;
4349 }
4350 if (type != TCPCHK_EXPECT_UNDEF) {
4351 memprintf(errmsg, "only on pattern expected");
4352 goto error;
4353 }
4354 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004355 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004356 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004357 int orig_arg = cur_arg;
4358
4359 if (proto != TCPCHK_RULES_HTTP_CHK)
4360 goto bad_tcp_kw;
4361 if (type != TCPCHK_EXPECT_UNDEF) {
4362 memprintf(errmsg, "only on pattern expected");
4363 goto error;
4364 }
4365 type = TCPCHK_EXPECT_HTTP_HEADER;
4366
Christopher Fauletb5594262020-05-05 20:23:13 +02004367 if (strcmp(args[cur_arg], "fhdr") == 0)
4368 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4369
Christopher Faulet39708192020-05-05 10:47:36 +02004370 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004371 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4372 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4373 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004374 args[orig_arg]);
4375 goto error;
4376 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004377
4378 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4379 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4380
Christopher Faulet39708192020-05-05 10:47:36 +02004381 cur_arg += 2;
4382 if (strcmp(args[cur_arg], "-m") == 0) {
4383 if (!*(args[cur_arg+1])) {
4384 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4385 args[orig_arg], args[cur_arg]);
4386 goto error;
4387 }
4388 if (strcmp(args[cur_arg+1], "str") == 0)
4389 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4390 else if (strcmp(args[cur_arg+1], "beg") == 0)
4391 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4392 else if (strcmp(args[cur_arg+1], "end") == 0)
4393 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4394 else if (strcmp(args[cur_arg+1], "sub") == 0)
4395 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004396 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4397 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4398 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4399 args[orig_arg]);
4400 goto error;
4401 }
Christopher Faulet39708192020-05-05 10:47:36 +02004402 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004403 }
Christopher Faulet39708192020-05-05 10:47:36 +02004404 else {
4405 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4406 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4407 goto error;
4408 }
4409 cur_arg += 2;
4410 }
4411 else
4412 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4413 npat = args[cur_arg];
4414
Christopher Fauletb5594262020-05-05 20:23:13 +02004415 if (!*(args[cur_arg+1]) ||
4416 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004417 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4418 goto next;
4419 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004420 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4421 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004422
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004423 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004424 if (strcmp(args[cur_arg+2], "-m") == 0) {
4425 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004426 if (!*(args[cur_arg+1])) {
4427 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4428 args[orig_arg], args[cur_arg]);
4429 goto error;
4430 }
4431 if (strcmp(args[cur_arg+1], "str") == 0)
4432 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4433 else if (strcmp(args[cur_arg+1], "beg") == 0)
4434 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4435 else if (strcmp(args[cur_arg+1], "end") == 0)
4436 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4437 else if (strcmp(args[cur_arg+1], "sub") == 0)
4438 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004439 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4440 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4441 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4442 args[orig_arg]);
4443 goto error;
4444 }
Christopher Faulet39708192020-05-05 10:47:36 +02004445 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004446 }
Christopher Faulet39708192020-05-05 10:47:36 +02004447 else {
4448 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4449 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4450 goto error;
4451 }
Christopher Faulet39708192020-05-05 10:47:36 +02004452 }
4453 else
4454 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004455
Christopher Fauletb5594262020-05-05 20:23:13 +02004456 if (!*(args[cur_arg+2])) {
4457 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4458 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004459 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004460 vpat = args[cur_arg+2];
4461 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004462 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004463 else if (strcmp(args[cur_arg], "comment") == 0) {
4464 if (in_pattern) {
4465 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4466 goto error;
4467 }
4468 if (!*(args[cur_arg+1])) {
4469 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4470 goto error;
4471 }
4472 cur_arg++;
4473 free(comment);
4474 comment = strdup(args[cur_arg]);
4475 if (!comment) {
4476 memprintf(errmsg, "out of memory");
4477 goto error;
4478 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004479 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004480 else if (strcmp(args[cur_arg], "on-success") == 0) {
4481 if (in_pattern) {
4482 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4483 goto error;
4484 }
4485 if (!*(args[cur_arg+1])) {
4486 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4487 goto error;
4488 }
4489 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004490 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004491 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004492 else if (strcmp(args[cur_arg], "on-error") == 0) {
4493 if (in_pattern) {
4494 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4495 goto error;
4496 }
4497 if (!*(args[cur_arg+1])) {
4498 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4499 goto error;
4500 }
4501 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004502 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004503 }
4504 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4505 if (in_pattern) {
4506 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4507 goto error;
4508 }
4509 if (!*(args[cur_arg+1])) {
4510 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4511 goto error;
4512 }
4513 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4514 ok_st = HCHK_STATUS_L7OKD;
4515 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4516 ok_st = HCHK_STATUS_L7OKCD;
4517 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4518 ok_st = HCHK_STATUS_L6OK;
4519 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4520 ok_st = HCHK_STATUS_L4OK;
4521 else {
4522 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4523 args[cur_arg], args[cur_arg+1]);
4524 goto error;
4525 }
4526 cur_arg++;
4527 }
4528 else if (strcmp(args[cur_arg], "error-status") == 0) {
4529 if (in_pattern) {
4530 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4531 goto error;
4532 }
4533 if (!*(args[cur_arg+1])) {
4534 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4535 goto error;
4536 }
4537 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4538 err_st = HCHK_STATUS_L7RSP;
4539 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4540 err_st = HCHK_STATUS_L7STS;
4541 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4542 err_st = HCHK_STATUS_L6RSP;
4543 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4544 err_st = HCHK_STATUS_L4CON;
4545 else {
4546 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4547 args[cur_arg], args[cur_arg+1]);
4548 goto error;
4549 }
4550 cur_arg++;
4551 }
4552 else if (strcmp(args[cur_arg], "status-code") == 0) {
4553 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004554
Christopher Faulet61cc8522020-04-20 14:54:42 +02004555 if (in_pattern) {
4556 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4557 goto error;
4558 }
4559 if (!*(args[cur_arg+1])) {
4560 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4561 goto error;
4562 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004563
Christopher Faulet61cc8522020-04-20 14:54:42 +02004564 cur_arg++;
4565 release_sample_expr(status_expr);
4566 px->conf.args.ctx = ARGC_SRV;
4567 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4568 file, line, errmsg, &px->conf.args, NULL);
4569 if (!status_expr) {
4570 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4571 goto error;
4572 }
4573 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4574 memprintf(errmsg, "error detected while parsing status-code expression : "
4575 " fetch method '%s' extracts information from '%s', "
4576 "none of which is available here.\n",
4577 args[cur_arg], sample_src_names(status_expr->fetch->use));
4578 goto error;
4579 }
4580 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4581 }
4582 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4583 if (in_pattern) {
4584 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4585 goto error;
4586 }
4587 if (!*(args[cur_arg+1])) {
4588 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4589 goto error;
4590 }
4591 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4592 tout_st = HCHK_STATUS_L7TOUT;
4593 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4594 tout_st = HCHK_STATUS_L6TOUT;
4595 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4596 tout_st = HCHK_STATUS_L4TOUT;
4597 else {
4598 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4599 args[cur_arg], args[cur_arg+1]);
4600 goto error;
4601 }
4602 cur_arg++;
4603 }
4604 else {
4605 if (proto == TCPCHK_RULES_HTTP_CHK) {
4606 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004607 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004608 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004609 }
4610 else {
4611 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004612 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4613 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004614 }
4615 goto error;
4616 }
Christopher Faulet39708192020-05-05 10:47:36 +02004617 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004618 cur_arg++;
4619 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004620
Christopher Faulet61cc8522020-04-20 14:54:42 +02004621 chk = calloc(1, sizeof(*chk));
4622 if (!chk) {
4623 memprintf(errmsg, "out of memory");
4624 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004625 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004626 chk->action = TCPCHK_ACT_EXPECT;
4627 LIST_INIT(&chk->expect.onerror_fmt);
4628 LIST_INIT(&chk->expect.onsuccess_fmt);
4629 chk->comment = comment; comment = NULL;
4630 chk->expect.type = type;
4631 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004632 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004633 chk->expect.ok_status = ok_st;
4634 chk->expect.err_status = err_st;
4635 chk->expect.tout_status = tout_st;
4636 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004637
Christopher Faulet61cc8522020-04-20 14:54:42 +02004638 if (on_success_msg) {
4639 px->conf.args.ctx = ARGC_SRV;
4640 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4641 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4642 goto error;
4643 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004644 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004645 if (on_error_msg) {
4646 px->conf.args.ctx = ARGC_SRV;
4647 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4648 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4649 goto error;
4650 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004651 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004652
Christopher Faulet61cc8522020-04-20 14:54:42 +02004653 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004654 case TCPCHK_EXPECT_HTTP_STATUS: {
4655 const char *p = pattern;
4656 unsigned int c1,c2;
4657
4658 chk->expect.codes.codes = NULL;
4659 chk->expect.codes.num = 0;
4660 while (1) {
4661 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4662 if (*p == '-') {
4663 p++;
4664 c2 = read_uint(&p, pattern + strlen(pattern));
4665 }
4666 if (c1 > c2) {
4667 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4668 goto error;
4669 }
4670
4671 chk->expect.codes.num++;
4672 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4673 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4674 if (!chk->expect.codes.codes) {
4675 memprintf(errmsg, "out of memory");
4676 goto error;
4677 }
4678 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4679 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4680
4681 if (*p == '\0')
4682 break;
4683 if (*p != ',') {
4684 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4685 goto error;
4686 }
4687 p++;
4688 }
4689 break;
4690 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004691 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004692 case TCPCHK_EXPECT_HTTP_BODY:
4693 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004694 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004695 memprintf(errmsg, "out of memory");
4696 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004697 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004698 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004699 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004700 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004701
4702 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004703 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4704 goto error;
4705 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004706 chk->expect.data.len = len;
4707 break;
4708 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004709 case TCPCHK_EXPECT_STRING_REGEX:
4710 case TCPCHK_EXPECT_BINARY_REGEX:
4711 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4712 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004713 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004714 if (!chk->expect.regex)
4715 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004716 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004717
4718 case TCPCHK_EXPECT_STRING_LF:
4719 case TCPCHK_EXPECT_BINARY_LF:
4720 case TCPCHK_EXPECT_HTTP_BODY_LF:
4721 LIST_INIT(&chk->expect.fmt);
4722 px->conf.args.ctx = ARGC_SRV;
4723 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4724 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4725 goto error;
4726 }
4727 break;
4728
Christopher Faulet39708192020-05-05 10:47:36 +02004729 case TCPCHK_EXPECT_HTTP_HEADER:
4730 if (!npat) {
4731 memprintf(errmsg, "unexpected error, undefined header name pattern");
4732 goto error;
4733 }
4734 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4735 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4736 if (!chk->expect.hdr.name_re)
4737 goto error;
4738 }
4739 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4740 px->conf.args.ctx = ARGC_SRV;
4741 LIST_INIT(&chk->expect.hdr.name_fmt);
4742 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4743 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4744 goto error;
4745 }
4746 }
4747 else {
4748 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4749 if (!isttest(chk->expect.hdr.name)) {
4750 memprintf(errmsg, "out of memory");
4751 goto error;
4752 }
4753 }
4754
4755 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4756 chk->expect.hdr.value = IST_NULL;
4757 break;
4758 }
4759
4760 if (!vpat) {
4761 memprintf(errmsg, "unexpected error, undefined header value pattern");
4762 goto error;
4763 }
4764 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4765 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4766 if (!chk->expect.hdr.value_re)
4767 goto error;
4768 }
4769 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4770 px->conf.args.ctx = ARGC_SRV;
4771 LIST_INIT(&chk->expect.hdr.value_fmt);
4772 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4773 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4774 goto error;
4775 }
4776 }
4777 else {
4778 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4779 if (!isttest(chk->expect.hdr.value)) {
4780 memprintf(errmsg, "out of memory");
4781 goto error;
4782 }
4783 }
4784
Christopher Faulet61cc8522020-04-20 14:54:42 +02004785 break;
4786 case TCPCHK_EXPECT_CUSTOM:
4787 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4788 break;
4789 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004790 memprintf(errmsg, "pattern not found");
4791 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004792 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004793
Christopher Faulet61cc8522020-04-20 14:54:42 +02004794 /* All tcp-check expect points back to the first inverse expect rule in
4795 * a chain of one or more expect rule, potentially itself.
4796 */
4797 chk->expect.head = chk;
4798 list_for_each_entry_rev(prev_check, rules, list) {
4799 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4800 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4801 chk->expect.head = prev_check;
4802 continue;
4803 }
4804 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4805 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004806 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004807 return chk;
4808
4809 error:
4810 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004811 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004812 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004813 return NULL;
4814}
4815
Christopher Faulet61cc8522020-04-20 14:54:42 +02004816/* Overwrites fields of the old http send rule with those of the new one. When
4817 * replaced, old values are freed and replaced by the new ones. New values are
4818 * not copied but transferred. At the end <new> should be empty and can be
4819 * safely released. This function never fails.
4820 */
4821static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004822{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004823 struct logformat_node *lf, *lfb;
4824 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004825
Christopher Faulet404f9192020-04-09 23:13:54 +02004826
Christopher Faulet61cc8522020-04-20 14:54:42 +02004827 if (new->send.http.meth.str.area) {
4828 free(old->send.http.meth.str.area);
4829 old->send.http.meth.meth = new->send.http.meth.meth;
4830 old->send.http.meth.str.area = new->send.http.meth.str.area;
4831 old->send.http.meth.str.data = new->send.http.meth.str.data;
4832 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004833 }
4834
Christopher Faulet61cc8522020-04-20 14:54:42 +02004835 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4836 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004837 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004838 else
4839 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4840 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4841 old->send.http.uri = new->send.http.uri;
4842 new->send.http.uri = IST_NULL;
4843 }
4844 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4845 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004846 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004847 else
4848 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4849 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4850 LIST_INIT(&old->send.http.uri_fmt);
4851 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4852 LIST_DEL(&lf->list);
4853 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4854 }
4855 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004856
Christopher Faulet61cc8522020-04-20 14:54:42 +02004857 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004858 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004859 old->send.http.vsn = new->send.http.vsn;
4860 new->send.http.vsn = IST_NULL;
4861 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004862
Christopher Faulet61cc8522020-04-20 14:54:42 +02004863 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4864 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4865 LIST_DEL(&hdr->list);
4866 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004867 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004868
4869 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4870 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004871 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004872 else
4873 free_tcpcheck_fmt(&old->send.http.body_fmt);
4874 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4875 old->send.http.body = new->send.http.body;
4876 new->send.http.body = IST_NULL;
4877 }
4878 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4879 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004880 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004881 else
4882 free_tcpcheck_fmt(&old->send.http.body_fmt);
4883 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4884 LIST_INIT(&old->send.http.body_fmt);
4885 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4886 LIST_DEL(&lf->list);
4887 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4888 }
4889 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004890}
4891
Christopher Faulet61cc8522020-04-20 14:54:42 +02004892/* Internal function used to add an http-check rule in a list during the config
4893 * parsing step. Depending on its type, and the previously inserted rules, a
4894 * specific action may be performed or an error may be reported. This functions
4895 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4896 * message.
4897 */
4898static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004899{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004900 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004901
Christopher Faulet61cc8522020-04-20 14:54:42 +02004902 /* the implicit send rule coming from an "option httpchk" line must be
4903 * merged with the first explici http-check send rule, if
4904 * any. Depdending the declaration order some tests are required.
4905 *
4906 * Some tests is also required for other kinds of http-check rules to be
4907 * sure the ruleset remains valid.
4908 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004909
Christopher Faulet61cc8522020-04-20 14:54:42 +02004910 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004911 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004912 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4913 * following tests are performed :
4914 *
4915 * 1- If there is no such rule or if it is not a send rule, the implicit send
4916 * rule is pushed in front of the ruleset
4917 *
4918 * 2- If it is another implicit send rule, it is replaced with the new one.
4919 *
4920 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004921 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004922 * new send rule (the implicit one).
4923 */
4924 r = get_first_tcpcheck_rule(rules);
4925 if (r && r->action == TCPCHK_ACT_CONNECT)
4926 r = get_next_tcpcheck_rule(rules, r);
4927 if (!r || r->action != TCPCHK_ACT_SEND)
4928 LIST_ADD(rules->list, &chk->list);
4929 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4930 LIST_DEL(&r->list);
4931 free_tcpcheck(r, 0);
4932 LIST_ADD(rules->list, &chk->list);
4933 }
4934 else {
4935 tcpcheck_overwrite_send_http_rule(r, chk);
4936 free_tcpcheck(chk, 0);
4937 }
4938 }
4939 else {
4940 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4941 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4942 * with an existing implicit send rule, if any. At the end, if there is no error,
4943 * the rule is appended to the list.
4944 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004945
Christopher Faulet61cc8522020-04-20 14:54:42 +02004946 r = get_last_tcpcheck_rule(rules);
4947 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4948 /* no error */;
4949 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4950 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4951 chk->index+1);
4952 return 0;
4953 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004954 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004955 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4956 chk->index+1);
4957 return 0;
4958 }
4959 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4960 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4961 chk->index+1);
4962 return 0;
4963 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004964
Christopher Faulet61cc8522020-04-20 14:54:42 +02004965 if (chk->action == TCPCHK_ACT_SEND) {
4966 r = get_first_tcpcheck_rule(rules);
4967 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4968 tcpcheck_overwrite_send_http_rule(r, chk);
4969 free_tcpcheck(chk, 0);
4970 LIST_DEL(&r->list);
4971 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4972 chk = r;
4973 }
4974 }
4975 LIST_ADDQ(rules->list, &chk->list);
4976 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004977 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004978}
4979
Christopher Faulet61cc8522020-04-20 14:54:42 +02004980/**************************************************************************/
4981/************************** Init/deinit checks ****************************/
4982/**************************************************************************/
4983static const char *init_check(struct check *check, int type)
4984{
4985 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004986
Christopher Faulet61cc8522020-04-20 14:54:42 +02004987 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4988 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004989
Christopher Faulet61cc8522020-04-20 14:54:42 +02004990 check->bi.area = calloc(check->bi.size, sizeof(char));
4991 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004992
Christopher Faulet61cc8522020-04-20 14:54:42 +02004993 if (!check->bi.area || !check->bo.area)
4994 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004995
Christopher Faulet61cc8522020-04-20 14:54:42 +02004996 check->wait_list.tasklet = tasklet_new();
4997 if (!check->wait_list.tasklet)
4998 return "out of memory while allocating check tasklet";
4999 check->wait_list.events = 0;
5000 check->wait_list.tasklet->process = event_srv_chk_io;
5001 check->wait_list.tasklet->context = check;
5002 return NULL;
5003}
5004
5005void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005006{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005007 task_destroy(check->task);
5008 if (check->wait_list.tasklet)
5009 tasklet_free(check->wait_list.tasklet);
5010
5011 free(check->bi.area);
5012 free(check->bo.area);
5013 if (check->cs) {
5014 free(check->cs->conn);
5015 check->cs->conn = NULL;
5016 cs_free(check->cs);
5017 check->cs = NULL;
5018 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005019}
5020
Christopher Faulet61cc8522020-04-20 14:54:42 +02005021/* manages a server health-check. Returns the time the task accepts to wait, or
5022 * TIME_ETERNITY for infinity.
5023 */
5024static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005025{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005026 struct check *check = context;
5027
5028 if (check->type == PR_O2_EXT_CHK)
5029 return process_chk_proc(t, context, state);
5030 return process_chk_conn(t, context, state);
5031
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005032}
5033
Christopher Faulet61cc8522020-04-20 14:54:42 +02005034
5035static int start_check_task(struct check *check, int mininter,
5036 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005037{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005038 struct task *t;
5039 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005040
Christopher Faulet61cc8522020-04-20 14:54:42 +02005041 if (check->type == PR_O2_EXT_CHK)
5042 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005043
Christopher Faulet61cc8522020-04-20 14:54:42 +02005044 /* task for the check */
5045 if ((t = task_new(thread_mask)) == NULL) {
5046 ha_alert("Starting [%s:%s] check: out of memory.\n",
5047 check->server->proxy->id, check->server->id);
5048 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005049 }
5050
Christopher Faulet61cc8522020-04-20 14:54:42 +02005051 check->task = t;
5052 t->process = process_chk;
5053 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005054
Christopher Faulet61cc8522020-04-20 14:54:42 +02005055 if (mininter < srv_getinter(check))
5056 mininter = srv_getinter(check);
5057
5058 if (global.max_spread_checks && mininter > global.max_spread_checks)
5059 mininter = global.max_spread_checks;
5060
5061 /* check this every ms */
5062 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5063 check->start = now;
5064 task_queue(t);
5065
5066 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005067}
5068
Christopher Faulet61cc8522020-04-20 14:54:42 +02005069/* updates the server's weight during a warmup stage. Once the final weight is
5070 * reached, the task automatically stops. Note that any server status change
5071 * must have updated s->last_change accordingly.
5072 */
5073static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005074{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005075 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005076
Christopher Faulet61cc8522020-04-20 14:54:42 +02005077 /* by default, plan on stopping the task */
5078 t->expire = TICK_ETERNITY;
5079 if ((s->next_admin & SRV_ADMF_MAINT) ||
5080 (s->next_state != SRV_ST_STARTING))
5081 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005082
Christopher Faulet61cc8522020-04-20 14:54:42 +02005083 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005084
Christopher Faulet61cc8522020-04-20 14:54:42 +02005085 /* recalculate the weights and update the state */
5086 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005087
Christopher Faulet61cc8522020-04-20 14:54:42 +02005088 /* probably that we can refill this server with a bit more connections */
5089 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005090
Christopher Faulet61cc8522020-04-20 14:54:42 +02005091 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005092
Christopher Faulet61cc8522020-04-20 14:54:42 +02005093 /* get back there in 1 second or 1/20th of the slowstart interval,
5094 * whichever is greater, resulting in small 5% steps.
5095 */
5096 if (s->next_state == SRV_ST_STARTING)
5097 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5098 return t;
5099}
5100
5101/*
5102 * Start health-check.
5103 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5104 */
5105static int start_checks()
5106{
5107
5108 struct proxy *px;
5109 struct server *s;
5110 struct task *t;
5111 int nbcheck=0, mininter=0, srvpos=0;
5112
5113 /* 0- init the dummy frontend used to create all checks sessions */
5114 init_new_proxy(&checks_fe);
5115 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5116 checks_fe.mode = PR_MODE_TCP;
5117 checks_fe.maxconn = 0;
5118 checks_fe.conn_retries = CONN_RETRIES;
5119 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5120 checks_fe.timeout.client = TICK_ETERNITY;
5121
5122 /* 1- count the checkers to run simultaneously.
5123 * We also determine the minimum interval among all of those which
5124 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5125 * will be used to spread their start-up date. Those which have
5126 * a shorter interval will start independently and will not dictate
5127 * too short an interval for all others.
5128 */
5129 for (px = proxies_list; px; px = px->next) {
5130 for (s = px->srv; s; s = s->next) {
5131 if (s->slowstart) {
5132 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5133 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5134 return ERR_ALERT | ERR_FATAL;
5135 }
5136 /* We need a warmup task that will be called when the server
5137 * state switches from down to up.
5138 */
5139 s->warmup = t;
5140 t->process = server_warmup;
5141 t->context = s;
5142 /* server can be in this state only because of */
5143 if (s->next_state == SRV_ST_STARTING)
5144 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 +02005145 }
5146
Christopher Faulet61cc8522020-04-20 14:54:42 +02005147 if (s->check.state & CHK_ST_CONFIGURED) {
5148 nbcheck++;
5149 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5150 (!mininter || mininter > srv_getinter(&s->check)))
5151 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005152 }
5153
Christopher Faulet61cc8522020-04-20 14:54:42 +02005154 if (s->agent.state & CHK_ST_CONFIGURED) {
5155 nbcheck++;
5156 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5157 (!mininter || mininter > srv_getinter(&s->agent)))
5158 mininter = srv_getinter(&s->agent);
5159 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005160 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005161 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005162
Christopher Faulet61cc8522020-04-20 14:54:42 +02005163 if (!nbcheck)
5164 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005165
Christopher Faulet61cc8522020-04-20 14:54:42 +02005166 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005167
Christopher Faulet61cc8522020-04-20 14:54:42 +02005168 /*
5169 * 2- start them as far as possible from each others. For this, we will
5170 * start them after their interval set to the min interval divided by
5171 * the number of servers, weighted by the server's position in the list.
5172 */
5173 for (px = proxies_list; px; px = px->next) {
5174 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5175 if (init_pid_list()) {
5176 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5177 return ERR_ALERT | ERR_FATAL;
5178 }
5179 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005180
Christopher Faulet61cc8522020-04-20 14:54:42 +02005181 for (s = px->srv; s; s = s->next) {
5182 /* A task for the main check */
5183 if (s->check.state & CHK_ST_CONFIGURED) {
5184 if (s->check.type == PR_O2_EXT_CHK) {
5185 if (!prepare_external_check(&s->check))
5186 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005187 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005188 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5189 return ERR_ALERT | ERR_FATAL;
5190 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005191 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005192
Christopher Faulet61cc8522020-04-20 14:54:42 +02005193 /* A task for a auxiliary agent check */
5194 if (s->agent.state & CHK_ST_CONFIGURED) {
5195 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5196 return ERR_ALERT | ERR_FATAL;
5197 }
5198 srvpos++;
5199 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005200 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005201 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005202 return 0;
5203}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005204
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005205
Christopher Faulet61cc8522020-04-20 14:54:42 +02005206/*
5207 * Return value:
5208 * the port to be used for the health check
5209 * 0 in case no port could be found for the check
5210 */
5211static int srv_check_healthcheck_port(struct check *chk)
5212{
5213 int i = 0;
5214 struct server *srv = NULL;
5215
5216 srv = chk->server;
5217
5218 /* by default, we use the health check port ocnfigured */
5219 if (chk->port > 0)
5220 return chk->port;
5221
5222 /* try to get the port from check_core.addr if check.port not set */
5223 i = get_host_port(&chk->addr);
5224 if (i > 0)
5225 return i;
5226
5227 /* try to get the port from server address */
5228 /* prevent MAPPORTS from working at this point, since checks could
5229 * not be performed in such case (MAPPORTS impose a relative ports
5230 * based on live traffic)
5231 */
5232 if (srv->flags & SRV_F_MAPPORTS)
5233 return 0;
5234
5235 i = srv->svc_port; /* by default */
5236 if (i > 0)
5237 return i;
5238
5239 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005240}
5241
Christopher Faulet61cc8522020-04-20 14:54:42 +02005242/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5243 * if an error occurred.
5244 */
5245static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005246{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005247 const char *err;
5248 struct tcpcheck_rule *r;
5249 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005250
Christopher Faulet61cc8522020-04-20 14:54:42 +02005251 if (!srv->do_check)
5252 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005253
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005254
Christopher Faulet61cc8522020-04-20 14:54:42 +02005255 /* If neither a port nor an addr was specified and no check transport
5256 * layer is forced, then the transport layer used by the checks is the
5257 * same as for the production traffic. Otherwise we use raw_sock by
5258 * default, unless one is specified.
5259 */
5260 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5261 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5262 srv->check.use_ssl = srv->use_ssl;
5263 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005264 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005265 else if (srv->check.use_ssl == 1)
5266 srv->check.xprt = xprt_get(XPRT_SSL);
5267 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005268 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02005269 else if (srv->check.use_ssl == 1)
5270 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005271
Christopher Faulet12882cf2020-04-23 15:50:18 +02005272 /* Inherit the mux protocol from the server if not already defined for
5273 * the check
5274 */
5275 if (srv->mux_proto && !srv->check.mux_proto)
5276 srv->check.mux_proto = srv->mux_proto;
5277
Christopher Faulet61cc8522020-04-20 14:54:42 +02005278 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005279
Christopher Faulet61cc8522020-04-20 14:54:42 +02005280 /* We need at least a service port, a check port or the first tcp-check
5281 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5282 */
5283 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5284 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5285 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005286
Christopher Faulet61cc8522020-04-20 14:54:42 +02005287 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5288 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5289 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5290 ret |= ERR_ALERT | ERR_ABORT;
5291 goto out;
5292 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005293
Christopher Faulet61cc8522020-04-20 14:54:42 +02005294 /* search the first action (connect / send / expect) in the list */
5295 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5296 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5297 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5298 "nor tcp_check rule 'connect' with port information.\n",
5299 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5300 ret |= ERR_ALERT | ERR_ABORT;
5301 goto out;
5302 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005303
Christopher Faulet61cc8522020-04-20 14:54:42 +02005304 /* scan the tcp-check ruleset to ensure a port has been configured */
5305 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5306 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5307 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5308 "and a tcp_check rule 'connect' with no port information.\n",
5309 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5310 ret |= ERR_ALERT | ERR_ABORT;
5311 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005312 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005313 }
5314
Christopher Faulet61cc8522020-04-20 14:54:42 +02005315 init:
5316 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5317 struct tcpcheck_ruleset *rs = NULL;
5318 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5319 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005320
Christopher Faulet61cc8522020-04-20 14:54:42 +02005321 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5322 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005323
Christopher Faulet61cc8522020-04-20 14:54:42 +02005324 rs = find_tcpcheck_ruleset("*tcp-check");
5325 if (!rs) {
5326 rs = create_tcpcheck_ruleset("*tcp-check");
5327 if (rs == NULL) {
5328 ha_alert("config: %s '%s': out of memory.\n",
5329 proxy_type_str(srv->proxy), srv->proxy->id);
5330 ret |= ERR_ALERT | ERR_FATAL;
5331 goto out;
5332 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005333 }
5334
Christopher Faulet61cc8522020-04-20 14:54:42 +02005335 free_tcpcheck_vars(&rules->preset_vars);
5336 rules->list = &rs->rules;
5337 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005338 }
5339
Christopher Faulet61cc8522020-04-20 14:54:42 +02005340 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5341 if (err) {
5342 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5343 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5344 ret |= ERR_ALERT | ERR_ABORT;
5345 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005346 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005347 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5348 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005349
Christopher Faulet61cc8522020-04-20 14:54:42 +02005350 out:
5351 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005352}
5353
Christopher Faulet61cc8522020-04-20 14:54:42 +02005354/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5355 * if an error occurred.
5356 */
5357static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005358{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005359 struct tcpcheck_rule *chk;
5360 const char *err;
5361 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005362
Christopher Faulet61cc8522020-04-20 14:54:42 +02005363 if (!srv->do_agent)
5364 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005365
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005366 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005367 * implicit one is inserted before all others.
5368 */
5369 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5370 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5371 chk = calloc(1, sizeof(*chk));
5372 if (!chk) {
5373 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5374 " to agent-check for server '%s' (out of memory).\n",
5375 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5376 ret |= ERR_ALERT | ERR_FATAL;
5377 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005378 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005379 chk->action = TCPCHK_ACT_CONNECT;
5380 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5381 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005382 }
5383
Christopher Faulete5870d82020-04-15 11:32:03 +02005384
Christopher Faulet61cc8522020-04-20 14:54:42 +02005385 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5386 if (err) {
5387 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5388 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5389 ret |= ERR_ALERT | ERR_ABORT;
5390 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005391 }
5392
Christopher Faulet61cc8522020-04-20 14:54:42 +02005393 if (!srv->agent.inter)
5394 srv->agent.inter = srv->check.inter;
5395
5396 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5397 global.maxsock++;
5398
5399 out:
5400 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005401}
5402
Christopher Faulet61cc8522020-04-20 14:54:42 +02005403/* Check tcp-check health-check configuration for the proxy <px>. */
5404static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005405{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005406 struct tcpcheck_rule *chk, *back;
5407 char *comment = NULL, *errmsg = NULL;
5408 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5409 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005410
Christopher Faulet61cc8522020-04-20 14:54:42 +02005411 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5412 deinit_proxy_tcpcheck(px);
5413 goto out;
5414 }
5415
5416 free(px->check_command);
5417 free(px->check_path);
5418 px->check_command = px->check_path = NULL;
5419
5420 if (!px->tcpcheck_rules.list) {
5421 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5422 ret |= ERR_ALERT | ERR_FATAL;
5423 goto out;
5424 }
5425
5426 /* HTTP ruleset only : */
5427 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5428 struct tcpcheck_rule *next;
5429
5430 /* move remaining implicit send rule from "option httpchk" line to the right place.
5431 * If such rule exists, it must be the first one. In this case, the rule is moved
5432 * after the first connect rule, if any. Otherwise, nothing is done.
5433 */
5434 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5435 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5436 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5437 if (next && next->action == TCPCHK_ACT_CONNECT) {
5438 LIST_DEL(&chk->list);
5439 LIST_ADD(&next->list, &chk->list);
5440 chk->index = next->index;
5441 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005442 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005443
5444 /* add implicit expect rule if the last one is a send. It is inherited from previous
5445 * versions where the http expect rule was optional. Now it is possible to chained
5446 * send/expect rules but the last expect may still be implicit.
5447 */
5448 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5449 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005450 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005451 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5452 px->conf.file, px->conf.line, &errmsg);
5453 if (!next) {
5454 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5455 "(%s).\n", px->id, errmsg);
5456 free(errmsg);
5457 ret |= ERR_ALERT | ERR_FATAL;
5458 goto out;
5459 }
5460 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5461 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005462 }
5463 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005464
5465 /* For all ruleset: */
5466
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005467 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005468 * implicit one is inserted before all others.
5469 */
5470 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5471 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5472 chk = calloc(1, sizeof(*chk));
5473 if (!chk) {
5474 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5475 "(out of memory).\n", px->id);
5476 ret |= ERR_ALERT | ERR_FATAL;
5477 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005478 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005479 chk->action = TCPCHK_ACT_CONNECT;
5480 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5481 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5482 }
5483
5484 /* Remove all comment rules. To do so, when a such rule is found, the
5485 * comment is assigned to the following rule(s).
5486 */
5487 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5488 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5489 free(comment);
5490 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005491 }
5492
Christopher Faulet61cc8522020-04-20 14:54:42 +02005493 prev_action = chk->action;
5494 switch (chk->action) {
5495 case TCPCHK_ACT_COMMENT:
5496 free(comment);
5497 comment = chk->comment;
5498 LIST_DEL(&chk->list);
5499 free(chk);
5500 break;
5501 case TCPCHK_ACT_CONNECT:
5502 if (!chk->comment && comment)
5503 chk->comment = strdup(comment);
5504 /* fall though */
5505 case TCPCHK_ACT_ACTION_KW:
5506 free(comment);
5507 comment = NULL;
5508 break;
5509 case TCPCHK_ACT_SEND:
5510 case TCPCHK_ACT_EXPECT:
5511 if (!chk->comment && comment)
5512 chk->comment = strdup(comment);
5513 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005514 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005515 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005516 free(comment);
5517 comment = NULL;
5518
5519 out:
5520 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005521}
5522
Christopher Faulet61cc8522020-04-20 14:54:42 +02005523void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005524{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005525 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5526 px->tcpcheck_rules.flags = 0;
5527 px->tcpcheck_rules.list = NULL;
5528}
Christopher Faulete5870d82020-04-15 11:32:03 +02005529
Christopher Faulet61cc8522020-04-20 14:54:42 +02005530static void deinit_srv_check(struct server *srv)
5531{
5532 if (srv->check.state & CHK_ST_CONFIGURED)
5533 free_check(&srv->check);
5534 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5535 srv->do_check = 0;
5536}
Christopher Faulete5870d82020-04-15 11:32:03 +02005537
Christopher Faulet61cc8522020-04-20 14:54:42 +02005538
5539static void deinit_srv_agent_check(struct server *srv)
5540{
5541 if (srv->agent.tcpcheck_rules) {
5542 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5543 free(srv->agent.tcpcheck_rules);
5544 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005545 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005546
Christopher Faulet61cc8522020-04-20 14:54:42 +02005547 if (srv->agent.state & CHK_ST_CONFIGURED)
5548 free_check(&srv->agent);
5549
5550 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5551 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005552}
5553
Christopher Faulet61cc8522020-04-20 14:54:42 +02005554static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005555{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005556 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005557 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005558 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005559
Christopher Fauletd7cee712020-04-21 13:45:00 +02005560 node = ebpt_first(&shared_tcpchecks);
5561 while (node) {
5562 next = ebpt_next(node);
5563 ebpt_delete(node);
5564 free(node->key);
5565 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005566 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5567 LIST_DEL(&r->list);
5568 free_tcpcheck(r, 0);
5569 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005570 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005571 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005572 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005573}
Christopher Faulete5870d82020-04-15 11:32:03 +02005574
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005575
Christopher Faulet61cc8522020-04-20 14:54:42 +02005576REGISTER_POST_SERVER_CHECK(init_srv_check);
5577REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5578REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5579REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005580
Christopher Faulet61cc8522020-04-20 14:54:42 +02005581REGISTER_SERVER_DEINIT(deinit_srv_check);
5582REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5583REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5584REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005585
Christopher Faulet61cc8522020-04-20 14:54:42 +02005586/**************************************************************************/
5587/****************************** Email alerts ******************************/
5588/* NOTE: It may be pertinent to use an applet to handle email alerts */
5589/* instead of a tcp-check ruleset */
5590/**************************************************************************/
5591void email_alert_free(struct email_alert *alert)
5592{
5593 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005594
Christopher Faulet61cc8522020-04-20 14:54:42 +02005595 if (!alert)
5596 return;
5597
5598 if (alert->rules.list) {
5599 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5600 LIST_DEL(&rule->list);
5601 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005602 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005603 free_tcpcheck_vars(&alert->rules.preset_vars);
5604 free(alert->rules.list);
5605 alert->rules.list = NULL;
5606 }
5607 pool_free(pool_head_email_alert, alert);
5608}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005609
Christopher Faulet61cc8522020-04-20 14:54:42 +02005610static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5611{
5612 struct check *check = context;
5613 struct email_alertq *q;
5614 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005615
Christopher Faulet61cc8522020-04-20 14:54:42 +02005616 q = container_of(check, typeof(*q), check);
5617
5618 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5619 while (1) {
5620 if (!(check->state & CHK_ST_ENABLED)) {
5621 if (LIST_ISEMPTY(&q->email_alerts)) {
5622 /* All alerts processed, queue the task */
5623 t->expire = TICK_ETERNITY;
5624 task_queue(t);
5625 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005626 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005627
5628 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5629 LIST_DEL(&alert->list);
5630 t->expire = now_ms;
5631 check->tcpcheck_rules = &alert->rules;
5632 check->status = HCHK_STATUS_INI;
5633 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005634 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005635
5636 process_chk(t, context, state);
5637 if (check->state & CHK_ST_INPROGRESS)
5638 break;
5639
5640 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5641 email_alert_free(alert);
5642 check->tcpcheck_rules = NULL;
5643 check->server = NULL;
5644 check->state &= ~CHK_ST_ENABLED;
5645 }
5646 end:
5647 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5648 return t;
5649}
5650
5651/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5652 *
5653 * The function returns 1 in success case, otherwise, it returns 0 and err is
5654 * filled.
5655 */
5656int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5657{
5658 struct mailer *mailer;
5659 struct email_alertq *queues;
5660 const char *err_str;
5661 int i = 0;
5662
5663 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5664 memprintf(err, "out of memory while allocating mailer alerts queues");
5665 goto fail_no_queue;
5666 }
5667
5668 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5669 struct email_alertq *q = &queues[i];
5670 struct check *check = &q->check;
5671 struct task *t;
5672
5673 LIST_INIT(&q->email_alerts);
5674 HA_SPIN_INIT(&q->lock);
5675 check->inter = mls->timeout.mail;
5676 check->rise = DEF_AGENT_RISETIME;
5677 check->proxy = p;
5678 check->fall = DEF_AGENT_FALLTIME;
5679 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5680 memprintf(err, "%s", err_str);
5681 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005682 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005683
5684 check->xprt = mailer->xprt;
5685 check->addr = mailer->addr;
5686 check->port = get_host_port(&mailer->addr);
5687
5688 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5689 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005690 goto error;
5691 }
5692
Christopher Faulet61cc8522020-04-20 14:54:42 +02005693 check->task = t;
5694 t->process = process_email_alert;
5695 t->context = check;
5696
5697 /* check this in one ms */
5698 t->expire = TICK_ETERNITY;
5699 check->start = now;
5700 task_queue(t);
5701 }
5702
5703 mls->users++;
5704 free(p->email_alert.mailers.name);
5705 p->email_alert.mailers.m = mls;
5706 p->email_alert.queues = queues;
5707 return 0;
5708
5709 error:
5710 for (i = 0; i < mls->count; i++) {
5711 struct email_alertq *q = &queues[i];
5712 struct check *check = &q->check;
5713
5714 free_check(check);
5715 }
5716 free(queues);
5717 fail_no_queue:
5718 return 1;
5719}
5720
5721static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5722{
5723 struct tcpcheck_rule *tcpcheck, *prev_check;
5724 struct tcpcheck_expect *expect;
5725
5726 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5727 return 0;
5728 memset(tcpcheck, 0, sizeof(*tcpcheck));
5729 tcpcheck->action = TCPCHK_ACT_EXPECT;
5730
5731 expect = &tcpcheck->expect;
5732 expect->type = TCPCHK_EXPECT_STRING;
5733 LIST_INIT(&expect->onerror_fmt);
5734 LIST_INIT(&expect->onsuccess_fmt);
5735 expect->ok_status = HCHK_STATUS_L7OKD;
5736 expect->err_status = HCHK_STATUS_L7RSP;
5737 expect->tout_status = HCHK_STATUS_L7TOUT;
5738 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005739 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005740 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5741 return 0;
5742 }
5743
5744 /* All tcp-check expect points back to the first inverse expect rule
5745 * in a chain of one or more expect rule, potentially itself.
5746 */
5747 tcpcheck->expect.head = tcpcheck;
5748 list_for_each_entry_rev(prev_check, rules->list, list) {
5749 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5750 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5751 tcpcheck->expect.head = prev_check;
5752 continue;
5753 }
5754 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5755 break;
5756 }
5757 LIST_ADDQ(rules->list, &tcpcheck->list);
5758 return 1;
5759}
5760
5761static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5762{
5763 struct tcpcheck_rule *tcpcheck;
5764 struct tcpcheck_send *send;
5765 const char *in;
5766 char *dst;
5767 int i;
5768
5769 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5770 return 0;
5771 memset(tcpcheck, 0, sizeof(*tcpcheck));
5772 tcpcheck->action = TCPCHK_ACT_SEND;
5773
5774 send = &tcpcheck->send;
5775 send->type = TCPCHK_SEND_STRING;
5776
5777 for (i = 0; strs[i]; i++)
5778 send->data.len += strlen(strs[i]);
5779
Christopher Fauletb61caf42020-04-21 10:57:42 +02005780 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005781 if (!isttest(send->data)) {
5782 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5783 return 0;
5784 }
5785
Christopher Fauletb61caf42020-04-21 10:57:42 +02005786 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005787 for (i = 0; strs[i]; i++)
5788 for (in = strs[i]; (*dst = *in++); dst++);
5789 *dst = 0;
5790
5791 LIST_ADDQ(rules->list, &tcpcheck->list);
5792 return 1;
5793}
5794
5795static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5796 struct email_alertq *q, const char *msg)
5797{
5798 struct email_alert *alert;
5799 struct tcpcheck_rule *tcpcheck;
5800 struct check *check = &q->check;
5801
5802 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5803 goto error;
5804 LIST_INIT(&alert->list);
5805 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5806 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5807 if (!alert->rules.list)
5808 goto error;
5809 LIST_INIT(alert->rules.list);
5810 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5811 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005812
Christopher Faulet61cc8522020-04-20 14:54:42 +02005813 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5814 goto error;
5815 memset(tcpcheck, 0, sizeof(*tcpcheck));
5816 tcpcheck->action = TCPCHK_ACT_CONNECT;
5817 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005818
Christopher Faulet61cc8522020-04-20 14:54:42 +02005819 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005820
Christopher Faulet61cc8522020-04-20 14:54:42 +02005821 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005822 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005823
5824 {
5825 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5826 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5827 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005828 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005829
Christopher Faulet61cc8522020-04-20 14:54:42 +02005830 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5831 goto error;
5832
5833 {
5834 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5835 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005836 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005837 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005838
5839 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5840 goto error;
5841
5842 {
5843 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5844 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005845 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005846 }
5847
Christopher Faulet61cc8522020-04-20 14:54:42 +02005848 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5849 goto error;
5850
5851 {
5852 const char * const strs[2] = { "DATA\r\n" };
5853 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005854 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005855 }
5856
5857 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5858 goto error;
5859
5860 {
5861 struct tm tm;
5862 char datestr[48];
5863 const char * const strs[18] = {
5864 "From: ", p->email_alert.from, "\r\n",
5865 "To: ", p->email_alert.to, "\r\n",
5866 "Date: ", datestr, "\r\n",
5867 "Subject: [HAproxy Alert] ", msg, "\r\n",
5868 "\r\n",
5869 msg, "\r\n",
5870 "\r\n",
5871 ".\r\n",
5872 NULL
5873 };
5874
5875 get_localtime(date.tv_sec, &tm);
5876
5877 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005878 goto error;
5879 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005880
5881 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005882 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005883 }
5884
5885 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005886 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005887
5888 {
5889 const char * const strs[2] = { "QUIT\r\n" };
5890 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5891 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005892 }
5893
Christopher Faulet61cc8522020-04-20 14:54:42 +02005894 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5895 goto error;
5896
5897 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5898 task_wakeup(check->task, TASK_WOKEN_MSG);
5899 LIST_ADDQ(&q->email_alerts, &alert->list);
5900 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5901 return 1;
5902
5903error:
5904 email_alert_free(alert);
5905 return 0;
5906}
5907
5908static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5909{
5910 int i;
5911 struct mailer *mailer;
5912
5913 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5914 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5915 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5916 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5917 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005918 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005919 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005920
Christopher Faulet61cc8522020-04-20 14:54:42 +02005921 return;
5922}
5923
5924/*
5925 * Send email alert if configured.
5926 */
5927void send_email_alert(struct server *s, int level, const char *format, ...)
5928{
5929 va_list argp;
5930 char buf[1024];
5931 int len;
5932 struct proxy *p = s->proxy;
5933
5934 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5935 return;
5936
5937 va_start(argp, format);
5938 len = vsnprintf(buf, sizeof(buf), format, argp);
5939 va_end(argp);
5940
5941 if (len < 0 || len >= sizeof(buf)) {
5942 ha_alert("Email alert [%s] could not format message\n", p->id);
5943 return;
5944 }
5945
5946 enqueue_email_alert(p, s, buf);
5947}
5948
5949/**************************************************************************/
5950/************************** Check sample fetches **************************/
5951/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005952
Christopher Faulet61cc8522020-04-20 14:54:42 +02005953static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005954 { /* END */ },
5955}};
5956
5957INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5958
5959
5960/**************************************************************************/
5961/************************ Check's parsing functions ***********************/
5962/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005963/* Parses the "tcp-check" proxy keyword */
5964static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5965 struct proxy *defpx, const char *file, int line,
5966 char **errmsg)
5967{
Christopher Faulet404f9192020-04-09 23:13:54 +02005968 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005969 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005970 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005971
5972 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5973 ret = 1;
5974
Christopher Faulet404f9192020-04-09 23:13:54 +02005975 /* Deduce the ruleset name from the proxy info */
5976 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5977 ((curpx == defpx) ? "defaults" : curpx->id),
5978 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005979
Christopher Faulet61cc8522020-04-20 14:54:42 +02005980 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005981 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005982 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005983 if (rs == NULL) {
5984 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005985 goto error;
5986 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005987 }
5988
Gaetan Rivet5301b012020-02-25 17:19:17 +01005989 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005990 if (!LIST_ISEMPTY(&rs->rules)) {
5991 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005992 index = chk->index + 1;
5993 }
5994
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005995 cur_arg = 1;
5996 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005997 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005998 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5999 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006000 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006001 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02006002 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006003 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006004 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006005 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01006006 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
6007
6008 if (!kw) {
6009 action_kw_tcp_check_build_list(&trash);
6010 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
6011 "%s%s. but got '%s'",
6012 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6013 goto error;
6014 }
Christopher Faulet404f9192020-04-09 23:13:54 +02006015 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006016 }
6017
6018 if (!chk) {
6019 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6020 goto error;
6021 }
Christopher Faulet528f4812020-04-28 10:47:28 +02006022 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006023
6024 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01006025 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02006026 LIST_ADDQ(&rs->rules, &chk->list);
6027
6028 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02006029 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006030 /* Use this ruleset if the proxy already has tcp-check enabled */
6031 curpx->tcpcheck_rules.list = &rs->rules;
6032 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
6033 }
6034 else {
6035 /* mark this ruleset as unused for now */
6036 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
6037 }
6038
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006039 return ret;
6040
6041 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006042 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006043 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006044 return -1;
6045}
6046
Christopher Faulet51b129f2020-04-09 15:54:18 +02006047/* Parses the "http-check" proxy keyword */
6048static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6049 struct proxy *defpx, const char *file, int line,
6050 char **errmsg)
6051{
Christopher Faulete5870d82020-04-15 11:32:03 +02006052 struct tcpcheck_ruleset *rs = NULL;
6053 struct tcpcheck_rule *chk = NULL;
6054 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006055
6056 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6057 ret = 1;
6058
6059 cur_arg = 1;
6060 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6061 /* enable a graceful server shutdown on an HTTP 404 response */
6062 curpx->options |= PR_O_DISABLE404;
6063 if (too_many_args(1, args, errmsg, NULL))
6064 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006065 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006066 }
6067 else if (strcmp(args[cur_arg], "send-state") == 0) {
6068 /* enable emission of the apparent state of a server in HTTP checks */
6069 curpx->options2 |= PR_O2_CHK_SNDST;
6070 if (too_many_args(1, args, errmsg, NULL))
6071 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006072 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006073 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006074
Christopher Faulete5870d82020-04-15 11:32:03 +02006075 /* Deduce the ruleset name from the proxy info */
6076 chunk_printf(&trash, "*http-check-%s_%s-%d",
6077 ((curpx == defpx) ? "defaults" : curpx->id),
6078 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006079
Christopher Faulet61cc8522020-04-20 14:54:42 +02006080 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006081 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006082 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006083 if (rs == NULL) {
6084 memprintf(errmsg, "out of memory.\n");
6085 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006086 }
6087 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006088
Christopher Faulete5870d82020-04-15 11:32:03 +02006089 index = 0;
6090 if (!LIST_ISEMPTY(&rs->rules)) {
6091 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6092 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6093 index = chk->index + 1;
6094 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006095
Christopher Faulete5870d82020-04-15 11:32:03 +02006096 if (strcmp(args[cur_arg], "connect") == 0)
6097 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6098 else if (strcmp(args[cur_arg], "send") == 0)
6099 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6100 else if (strcmp(args[cur_arg], "expect") == 0)
6101 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6102 file, line, errmsg);
6103 else if (strcmp(args[cur_arg], "comment") == 0)
6104 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6105 else {
6106 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006107
Christopher Faulete5870d82020-04-15 11:32:03 +02006108 if (!kw) {
6109 action_kw_tcp_check_build_list(&trash);
6110 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6111 " 'send', 'expect'%s%s. but got '%s'",
6112 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6113 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006114 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006115 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6116 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006117
Christopher Faulete5870d82020-04-15 11:32:03 +02006118 if (!chk) {
6119 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6120 goto error;
6121 }
6122 ret = (*errmsg != NULL); /* Handle warning */
6123
6124 chk->index = index;
6125 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6126 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6127 /* Use this ruleset if the proxy already has http-check enabled */
6128 curpx->tcpcheck_rules.list = &rs->rules;
6129 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6130 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6131 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6132 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006133 goto error;
6134 }
6135 }
6136 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006137 /* mark this ruleset as unused for now */
6138 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6139 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006140 }
6141
Christopher Faulete5870d82020-04-15 11:32:03 +02006142 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006143 return ret;
6144
6145 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006146 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006147 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006148 return -1;
6149}
6150
Christopher Faulete9111b62020-04-09 18:12:08 +02006151/* Parses the "external-check" proxy keyword */
6152static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6153 struct proxy *defpx, const char *file, int line,
6154 char **errmsg)
6155{
6156 int cur_arg, ret = 0;
6157
6158 cur_arg = 1;
6159 if (!*(args[cur_arg])) {
6160 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6161 goto error;
6162 }
6163
6164 if (strcmp(args[cur_arg], "command") == 0) {
6165 if (too_many_args(2, args, errmsg, NULL))
6166 goto error;
6167 if (!*(args[cur_arg+1])) {
6168 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6169 goto error;
6170 }
6171 free(curpx->check_command);
6172 curpx->check_command = strdup(args[cur_arg+1]);
6173 }
6174 else if (strcmp(args[cur_arg], "path") == 0) {
6175 if (too_many_args(2, args, errmsg, NULL))
6176 goto error;
6177 if (!*(args[cur_arg+1])) {
6178 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6179 goto error;
6180 }
6181 free(curpx->check_path);
6182 curpx->check_path = strdup(args[cur_arg+1]);
6183 }
6184 else {
6185 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6186 args[0], args[1]);
6187 goto error;
6188 }
6189
6190 ret = (*errmsg != NULL); /* Handle warning */
6191 return ret;
6192
6193error:
6194 return -1;
6195}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006196
Christopher Faulet430e4802020-04-09 15:28:16 +02006197/* Parses the "option tcp-check" proxy keyword */
6198int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6199 const char *file, int line)
6200{
Christopher Faulet404f9192020-04-09 23:13:54 +02006201 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006202 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6203 int err_code = 0;
6204
6205 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6206 err_code |= ERR_WARN;
6207
6208 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6209 goto out;
6210
Christopher Faulet404f9192020-04-09 23:13:54 +02006211 curpx->options2 &= ~PR_O2_CHK_ANY;
6212 curpx->options2 |= PR_O2_TCPCHK_CHK;
6213
Christopher Fauletd7e63962020-04-17 20:15:59 +02006214 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006215 /* If a tcp-check rulesset is already set, do nothing */
6216 if (rules->list)
6217 goto out;
6218
6219 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6220 * get it.
6221 */
6222 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6223 goto curpx_ruleset;
6224
6225 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6226 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006227 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006228 if (rs)
6229 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006230 }
6231
Christopher Faulet404f9192020-04-09 23:13:54 +02006232 curpx_ruleset:
6233 /* Deduce the ruleset name from the proxy info */
6234 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6235 ((curpx == defpx) ? "defaults" : curpx->id),
6236 curpx->conf.file, curpx->conf.line);
6237
Christopher Faulet61cc8522020-04-20 14:54:42 +02006238 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006239 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006240 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006241 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006242 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6243 goto error;
6244 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006245 }
6246
Christopher Faulet404f9192020-04-09 23:13:54 +02006247 ruleset_found:
6248 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006249 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006250 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006251
6252 out:
6253 return err_code;
6254
6255 error:
6256 err_code |= ERR_ALERT | ERR_FATAL;
6257 goto out;
6258}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006259
6260/* Parses the "option redis-check" proxy keyword */
6261int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6262 const char *file, int line)
6263{
6264 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6265 static char *redis_res = "+PONG\r\n";
6266
6267 struct tcpcheck_ruleset *rs = NULL;
6268 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6269 struct tcpcheck_rule *chk;
6270 char *errmsg = NULL;
6271 int err_code = 0;
6272
6273 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6274 err_code |= ERR_WARN;
6275
6276 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6277 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006278
6279 curpx->options2 &= ~PR_O2_CHK_ANY;
6280 curpx->options2 |= PR_O2_TCPCHK_CHK;
6281
6282 free_tcpcheck_vars(&rules->preset_vars);
6283 rules->list = NULL;
6284 rules->flags = 0;
6285
Christopher Faulet61cc8522020-04-20 14:54:42 +02006286 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006287 if (rs)
6288 goto ruleset_found;
6289
Christopher Faulet61cc8522020-04-20 14:54:42 +02006290 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006291 if (rs == NULL) {
6292 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6293 goto error;
6294 }
6295
6296 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6297 1, curpx, &rs->rules, file, line, &errmsg);
6298 if (!chk) {
6299 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6300 goto error;
6301 }
6302 chk->index = 0;
6303 LIST_ADDQ(&rs->rules, &chk->list);
6304
6305 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6306 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006307 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006308 "on-success", "Redis server is ok",
6309 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006310 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006311 if (!chk) {
6312 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6313 goto error;
6314 }
6315 chk->index = 1;
6316 LIST_ADDQ(&rs->rules, &chk->list);
6317
Christopher Fauletd7cee712020-04-21 13:45:00 +02006318 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006319
6320 ruleset_found:
6321 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006322 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006323
6324 out:
6325 free(errmsg);
6326 return err_code;
6327
6328 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006329 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006330 err_code |= ERR_ALERT | ERR_FATAL;
6331 goto out;
6332}
6333
Christopher Faulet811f78c2020-04-01 11:10:27 +02006334
6335/* Parses the "option ssl-hello-chk" proxy keyword */
6336int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6337 const char *file, int line)
6338{
6339 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6340 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6341 *
6342 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6343 */
6344 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006345 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006346 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6347 "0079" /* ContentLength : 0x79 bytes after this one */
6348 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6349 "000075" /* HandshakeLength : 0x75 bytes after this one */
6350 "0300" /* Hello Version : 0x0300 = v3 */
6351 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6352 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6353 "00" /* Session ID length : empty (no session ID) */
6354 "004E" /* Cipher Suite Length : 78 bytes after this one */
6355 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6356 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6357 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6358 "000D" "000E" "000F" "0010" /* various bit lengths, */
6359 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6360 "0015" "0016" "0017" "0018"
6361 "0019" "001A" "001B" "002F"
6362 "0030" "0031" "0032" "0033"
6363 "0034" "0035" "0036" "0037"
6364 "0038" "0039" "003A"
6365 "01" /* Compression Length : 0x01 = 1 byte for types */
6366 "00" /* Compression Type : 0x00 = NULL compression */
6367 };
6368
6369 struct tcpcheck_ruleset *rs = NULL;
6370 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6371 struct tcpcheck_rule *chk;
6372 char *errmsg = NULL;
6373 int err_code = 0;
6374
6375 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6376 err_code |= ERR_WARN;
6377
6378 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6379 goto out;
6380
Christopher Faulet811f78c2020-04-01 11:10:27 +02006381 curpx->options2 &= ~PR_O2_CHK_ANY;
6382 curpx->options2 |= PR_O2_TCPCHK_CHK;
6383
6384 free_tcpcheck_vars(&rules->preset_vars);
6385 rules->list = NULL;
6386 rules->flags = 0;
6387
Christopher Faulet61cc8522020-04-20 14:54:42 +02006388 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006389 if (rs)
6390 goto ruleset_found;
6391
Christopher Faulet61cc8522020-04-20 14:54:42 +02006392 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006393 if (rs == NULL) {
6394 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6395 goto error;
6396 }
6397
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006398 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006399 1, curpx, &rs->rules, file, line, &errmsg);
6400 if (!chk) {
6401 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6402 goto error;
6403 }
6404 chk->index = 0;
6405 LIST_ADDQ(&rs->rules, &chk->list);
6406
6407 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006408 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006409 "error-status", "L6RSP", "tout-status", "L6TOUT",
6410 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006411 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006412 if (!chk) {
6413 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6414 goto error;
6415 }
6416 chk->index = 1;
6417 LIST_ADDQ(&rs->rules, &chk->list);
6418
Christopher Fauletd7cee712020-04-21 13:45:00 +02006419 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006420
6421 ruleset_found:
6422 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006423 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006424
6425 out:
6426 free(errmsg);
6427 return err_code;
6428
6429 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006430 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006431 err_code |= ERR_ALERT | ERR_FATAL;
6432 goto out;
6433}
6434
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006435/* Parses the "option smtpchk" proxy keyword */
6436int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6437 const char *file, int line)
6438{
6439 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6440
6441 struct tcpcheck_ruleset *rs = NULL;
6442 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6443 struct tcpcheck_rule *chk;
6444 struct tcpcheck_var *var = NULL;
6445 char *cmd = NULL, *errmsg = NULL;
6446 int err_code = 0;
6447
6448 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6449 err_code |= ERR_WARN;
6450
6451 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6452 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006453
6454 curpx->options2 &= ~PR_O2_CHK_ANY;
6455 curpx->options2 |= PR_O2_TCPCHK_CHK;
6456
6457 free_tcpcheck_vars(&rules->preset_vars);
6458 rules->list = NULL;
6459 rules->flags = 0;
6460
6461 cur_arg += 2;
6462 if (*args[cur_arg] && *args[cur_arg+1] &&
6463 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6464 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6465 if (cmd)
6466 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6467 }
6468 else {
6469 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6470 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6471 cmd = strdup("HELO localhost");
6472 }
6473
Christopher Fauletb61caf42020-04-21 10:57:42 +02006474 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006475 if (cmd == NULL || var == NULL) {
6476 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6477 goto error;
6478 }
6479 var->data.type = SMP_T_STR;
6480 var->data.u.str.area = cmd;
6481 var->data.u.str.data = strlen(cmd);
6482 LIST_INIT(&var->list);
6483 LIST_ADDQ(&rules->preset_vars, &var->list);
6484 cmd = NULL;
6485 var = NULL;
6486
Christopher Faulet61cc8522020-04-20 14:54:42 +02006487 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006488 if (rs)
6489 goto ruleset_found;
6490
Christopher Faulet61cc8522020-04-20 14:54:42 +02006491 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006492 if (rs == NULL) {
6493 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6494 goto error;
6495 }
6496
6497 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6498 1, curpx, &rs->rules, file, line, &errmsg);
6499 if (!chk) {
6500 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6501 goto error;
6502 }
6503 chk->index = 0;
6504 LIST_ADDQ(&rs->rules, &chk->list);
6505
6506 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6507 "min-recv", "4",
6508 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006509 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006510 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006511 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006512 if (!chk) {
6513 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6514 goto error;
6515 }
6516 chk->index = 1;
6517 LIST_ADDQ(&rs->rules, &chk->list);
6518
6519 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6520 "min-recv", "4",
6521 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006522 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6523 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006524 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006525 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006526 if (!chk) {
6527 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6528 goto error;
6529 }
6530 chk->index = 2;
6531 LIST_ADDQ(&rs->rules, &chk->list);
6532
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006533 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006534 1, curpx, &rs->rules, file, line, &errmsg);
6535 if (!chk) {
6536 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6537 goto error;
6538 }
6539 chk->index = 3;
6540 LIST_ADDQ(&rs->rules, &chk->list);
6541
6542 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6543 "min-recv", "4",
6544 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006545 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6546 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6547 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006548 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006549 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006550 if (!chk) {
6551 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6552 goto error;
6553 }
6554 chk->index = 4;
6555 LIST_ADDQ(&rs->rules, &chk->list);
6556
Christopher Fauletd7cee712020-04-21 13:45:00 +02006557 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006558
6559 ruleset_found:
6560 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006561 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006562
6563 out:
6564 free(errmsg);
6565 return err_code;
6566
6567 error:
6568 free(cmd);
6569 free(var);
6570 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006571 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006572 err_code |= ERR_ALERT | ERR_FATAL;
6573 goto out;
6574}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006575
Christopher Fauletce355072020-04-02 11:44:39 +02006576/* Parses the "option pgsql-check" proxy keyword */
6577int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6578 const char *file, int line)
6579{
6580 static char pgsql_req[] = {
6581 "%[var(check.plen),htonl,hex]" /* The packet length*/
6582 "00030000" /* the version 3.0 */
6583 "7573657200" /* "user" key */
6584 "%[var(check.username),hex]00" /* the username */
6585 "00"
6586 };
6587
6588 struct tcpcheck_ruleset *rs = NULL;
6589 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6590 struct tcpcheck_rule *chk;
6591 struct tcpcheck_var *var = NULL;
6592 char *user = NULL, *errmsg = NULL;
6593 size_t packetlen = 0;
6594 int err_code = 0;
6595
6596 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6597 err_code |= ERR_WARN;
6598
6599 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6600 goto out;
6601
Christopher Fauletce355072020-04-02 11:44:39 +02006602 curpx->options2 &= ~PR_O2_CHK_ANY;
6603 curpx->options2 |= PR_O2_TCPCHK_CHK;
6604
6605 free_tcpcheck_vars(&rules->preset_vars);
6606 rules->list = NULL;
6607 rules->flags = 0;
6608
6609 cur_arg += 2;
6610 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6611 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6612 file, line, args[0], args[1]);
6613 goto error;
6614 }
6615 if (strcmp(args[cur_arg], "user") == 0) {
6616 packetlen = 15 + strlen(args[cur_arg+1]);
6617 user = strdup(args[cur_arg+1]);
6618
Christopher Fauletb61caf42020-04-21 10:57:42 +02006619 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006620 if (user == NULL || var == NULL) {
6621 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6622 goto error;
6623 }
6624 var->data.type = SMP_T_STR;
6625 var->data.u.str.area = user;
6626 var->data.u.str.data = strlen(user);
6627 LIST_INIT(&var->list);
6628 LIST_ADDQ(&rules->preset_vars, &var->list);
6629 user = NULL;
6630 var = NULL;
6631
Christopher Fauletb61caf42020-04-21 10:57:42 +02006632 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006633 if (var == NULL) {
6634 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6635 goto error;
6636 }
6637 var->data.type = SMP_T_SINT;
6638 var->data.u.sint = packetlen;
6639 LIST_INIT(&var->list);
6640 LIST_ADDQ(&rules->preset_vars, &var->list);
6641 var = NULL;
6642 }
6643 else {
6644 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6645 file, line, args[0], args[1]);
6646 goto error;
6647 }
6648
Christopher Faulet61cc8522020-04-20 14:54:42 +02006649 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006650 if (rs)
6651 goto ruleset_found;
6652
Christopher Faulet61cc8522020-04-20 14:54:42 +02006653 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006654 if (rs == NULL) {
6655 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6656 goto error;
6657 }
6658
6659 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6660 1, curpx, &rs->rules, file, line, &errmsg);
6661 if (!chk) {
6662 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6663 goto error;
6664 }
6665 chk->index = 0;
6666 LIST_ADDQ(&rs->rules, &chk->list);
6667
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006668 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006669 1, curpx, &rs->rules, file, line, &errmsg);
6670 if (!chk) {
6671 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6672 goto error;
6673 }
6674 chk->index = 1;
6675 LIST_ADDQ(&rs->rules, &chk->list);
6676
6677 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6678 "min-recv", "5",
6679 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006680 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006681 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006682 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006683 if (!chk) {
6684 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6685 goto error;
6686 }
6687 chk->index = 2;
6688 LIST_ADDQ(&rs->rules, &chk->list);
6689
Christopher Fauletb841c742020-04-27 18:29:49 +02006690 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 +02006691 "min-recv", "9",
6692 "error-status", "L7STS",
6693 "on-success", "PostgreSQL server is ok",
6694 "on-error", "PostgreSQL unknown error",
6695 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006696 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006697 if (!chk) {
6698 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6699 goto error;
6700 }
6701 chk->index = 3;
6702 LIST_ADDQ(&rs->rules, &chk->list);
6703
Christopher Fauletd7cee712020-04-21 13:45:00 +02006704 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletce355072020-04-02 11:44:39 +02006705
6706 ruleset_found:
6707 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006708 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006709
6710 out:
6711 free(errmsg);
6712 return err_code;
6713
6714 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006715 free(user);
6716 free(var);
6717 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006718 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006719 err_code |= ERR_ALERT | ERR_FATAL;
6720 goto out;
6721}
6722
6723
6724/* Parses the "option mysql-check" proxy keyword */
6725int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6726 const char *file, int line)
6727{
6728 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6729 * const char mysql40_client_auth_pkt[] = {
6730 * "\x0e\x00\x00" // packet length
6731 * "\x01" // packet number
6732 * "\x00\x00" // client capabilities
6733 * "\x00\x00\x01" // max packet
6734 * "haproxy\x00" // username (null terminated string)
6735 * "\x00" // filler (always 0x00)
6736 * "\x01\x00\x00" // packet length
6737 * "\x00" // packet number
6738 * "\x01" // COM_QUIT command
6739 * };
6740 */
6741 static char mysql40_rsname[] = "*mysql40-check";
6742 static char mysql40_req[] = {
6743 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6744 "0080" /* client capabilities */
6745 "000001" /* max packet */
6746 "%[var(check.username),hex]00" /* the username */
6747 "00" /* filler (always 0x00) */
6748 "010000" /* packet length*/
6749 "00" /* sequence ID */
6750 "01" /* COM_QUIT command */
6751 };
6752
6753 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6754 * const char mysql41_client_auth_pkt[] = {
6755 * "\x0e\x00\x00\" // packet length
6756 * "\x01" // packet number
6757 * "\x00\x00\x00\x00" // client capabilities
6758 * "\x00\x00\x00\x01" // max packet
6759 * "\x21" // character set (UTF-8)
6760 * char[23] // All zeroes
6761 * "haproxy\x00" // username (null terminated string)
6762 * "\x00" // filler (always 0x00)
6763 * "\x01\x00\x00" // packet length
6764 * "\x00" // packet number
6765 * "\x01" // COM_QUIT command
6766 * };
6767 */
6768 static char mysql41_rsname[] = "*mysql41-check";
6769 static char mysql41_req[] = {
6770 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6771 "00820000" /* client capabilities */
6772 "00800001" /* max packet */
6773 "21" /* character set (UTF-8) */
6774 "000000000000000000000000" /* 23 bytes, al zeroes */
6775 "0000000000000000000000"
6776 "%[var(check.username),hex]00" /* the username */
6777 "00" /* filler (always 0x00) */
6778 "010000" /* packet length*/
6779 "00" /* sequence ID */
6780 "01" /* COM_QUIT command */
6781 };
6782
6783 struct tcpcheck_ruleset *rs = NULL;
6784 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6785 struct tcpcheck_rule *chk;
6786 struct tcpcheck_var *var = NULL;
6787 char *mysql_rsname = "*mysql-check";
6788 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6789 int index = 0, err_code = 0;
6790
6791 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6792 err_code |= ERR_WARN;
6793
6794 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6795 goto out;
6796
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006797 curpx->options2 &= ~PR_O2_CHK_ANY;
6798 curpx->options2 |= PR_O2_TCPCHK_CHK;
6799
6800 free_tcpcheck_vars(&rules->preset_vars);
6801 rules->list = NULL;
6802 rules->flags = 0;
6803
6804 cur_arg += 2;
6805 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006806 int packetlen, userlen;
6807
6808 if (strcmp(args[cur_arg], "user") != 0) {
6809 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6810 file, line, args[0], args[1], args[cur_arg]);
6811 goto error;
6812 }
6813
6814 if (*(args[cur_arg+1]) == 0) {
6815 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6816 file, line, args[0], args[1], args[cur_arg]);
6817 goto error;
6818 }
6819
6820 hdr = calloc(4, sizeof(*hdr));
6821 user = strdup(args[cur_arg+1]);
6822 userlen = strlen(args[cur_arg+1]);
6823
6824 if (hdr == NULL || user == NULL) {
6825 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6826 goto error;
6827 }
6828
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006829 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006830 packetlen = userlen + 7 + 27;
6831 mysql_req = mysql41_req;
6832 mysql_rsname = mysql41_rsname;
6833 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006834 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006835 packetlen = userlen + 7;
6836 mysql_req = mysql40_req;
6837 mysql_rsname = mysql40_rsname;
6838 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006839 else {
6840 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
6841 file, line, args[cur_arg], args[cur_arg+2]);
6842 goto error;
6843 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006844
6845 hdr[0] = (unsigned char)(packetlen & 0xff);
6846 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6847 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6848 hdr[3] = 1;
6849
Christopher Fauletb61caf42020-04-21 10:57:42 +02006850 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006851 if (var == NULL) {
6852 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6853 goto error;
6854 }
6855 var->data.type = SMP_T_STR;
6856 var->data.u.str.area = hdr;
6857 var->data.u.str.data = 4;
6858 LIST_INIT(&var->list);
6859 LIST_ADDQ(&rules->preset_vars, &var->list);
6860 hdr = NULL;
6861 var = NULL;
6862
Christopher Fauletb61caf42020-04-21 10:57:42 +02006863 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006864 if (var == NULL) {
6865 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6866 goto error;
6867 }
6868 var->data.type = SMP_T_STR;
6869 var->data.u.str.area = user;
6870 var->data.u.str.data = strlen(user);
6871 LIST_INIT(&var->list);
6872 LIST_ADDQ(&rules->preset_vars, &var->list);
6873 user = NULL;
6874 var = NULL;
6875 }
6876
Christopher Faulet61cc8522020-04-20 14:54:42 +02006877 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006878 if (rs)
6879 goto ruleset_found;
6880
Christopher Faulet61cc8522020-04-20 14:54:42 +02006881 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006882 if (rs == NULL) {
6883 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6884 goto error;
6885 }
6886
6887 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6888 1, curpx, &rs->rules, file, line, &errmsg);
6889 if (!chk) {
6890 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6891 goto error;
6892 }
6893 chk->index = index++;
6894 LIST_ADDQ(&rs->rules, &chk->list);
6895
6896 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006897 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006898 1, curpx, &rs->rules, file, line, &errmsg);
6899 if (!chk) {
6900 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6901 goto error;
6902 }
6903 chk->index = index++;
6904 LIST_ADDQ(&rs->rules, &chk->list);
6905 }
6906
6907 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006908 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006909 if (!chk) {
6910 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6911 goto error;
6912 }
6913 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6914 chk->index = index++;
6915 LIST_ADDQ(&rs->rules, &chk->list);
6916
6917 if (mysql_req) {
6918 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006919 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006920 if (!chk) {
6921 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6922 goto error;
6923 }
6924 chk->expect.custom = tcpcheck_mysql_expect_ok;
6925 chk->index = index++;
6926 LIST_ADDQ(&rs->rules, &chk->list);
6927 }
6928
Christopher Fauletd7cee712020-04-21 13:45:00 +02006929 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006930
6931 ruleset_found:
6932 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006933 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006934
6935 out:
6936 free(errmsg);
6937 return err_code;
6938
6939 error:
6940 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006941 free(user);
6942 free(var);
6943 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006944 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006945 err_code |= ERR_ALERT | ERR_FATAL;
6946 goto out;
6947}
6948
Christopher Faulet1997eca2020-04-03 23:13:50 +02006949int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6950 const char *file, int line)
6951{
6952 static char *ldap_req = "300C020101600702010304008000";
6953
6954 struct tcpcheck_ruleset *rs = NULL;
6955 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6956 struct tcpcheck_rule *chk;
6957 char *errmsg = NULL;
6958 int err_code = 0;
6959
6960 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6961 err_code |= ERR_WARN;
6962
6963 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6964 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006965
6966 curpx->options2 &= ~PR_O2_CHK_ANY;
6967 curpx->options2 |= PR_O2_TCPCHK_CHK;
6968
6969 free_tcpcheck_vars(&rules->preset_vars);
6970 rules->list = NULL;
6971 rules->flags = 0;
6972
Christopher Faulet61cc8522020-04-20 14:54:42 +02006973 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006974 if (rs)
6975 goto ruleset_found;
6976
Christopher Faulet61cc8522020-04-20 14:54:42 +02006977 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006978 if (rs == NULL) {
6979 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6980 goto error;
6981 }
6982
6983 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6984 1, curpx, &rs->rules, file, line, &errmsg);
6985 if (!chk) {
6986 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6987 goto error;
6988 }
6989 chk->index = 0;
6990 LIST_ADDQ(&rs->rules, &chk->list);
6991
6992 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6993 "min-recv", "14",
6994 "on-error", "Not LDAPv3 protocol",
6995 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006996 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006997 if (!chk) {
6998 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6999 goto error;
7000 }
7001 chk->index = 1;
7002 LIST_ADDQ(&rs->rules, &chk->list);
7003
7004 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007005 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007006 if (!chk) {
7007 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7008 goto error;
7009 }
7010 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
7011 chk->index = 2;
7012 LIST_ADDQ(&rs->rules, &chk->list);
7013
Christopher Fauletd7cee712020-04-21 13:45:00 +02007014 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007015
7016 ruleset_found:
7017 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007018 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02007019
7020 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02007021 free(errmsg);
7022 return err_code;
7023
7024 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007025 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007026 err_code |= ERR_ALERT | ERR_FATAL;
7027 goto out;
7028}
7029
7030int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7031 const char *file, int line)
7032{
7033 struct tcpcheck_ruleset *rs = NULL;
7034 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7035 struct tcpcheck_rule *chk;
7036 char *spop_req = NULL;
7037 char *errmsg = NULL;
7038 int spop_len = 0, err_code = 0;
7039
7040 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7041 err_code |= ERR_WARN;
7042
7043 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7044 goto out;
7045
Christopher Faulet267b01b2020-04-04 10:27:09 +02007046 curpx->options2 &= ~PR_O2_CHK_ANY;
7047 curpx->options2 |= PR_O2_TCPCHK_CHK;
7048
7049 free_tcpcheck_vars(&rules->preset_vars);
7050 rules->list = NULL;
7051 rules->flags = 0;
7052
7053
Christopher Faulet61cc8522020-04-20 14:54:42 +02007054 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007055 if (rs)
7056 goto ruleset_found;
7057
Christopher Faulet61cc8522020-04-20 14:54:42 +02007058 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007059 if (rs == NULL) {
7060 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7061 goto error;
7062 }
7063
7064 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7065 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7066 goto error;
7067 }
7068 chunk_reset(&trash);
7069 dump_binary(&trash, spop_req, spop_len);
7070 trash.area[trash.data] = '\0';
7071
7072 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7073 1, curpx, &rs->rules, file, line, &errmsg);
7074 if (!chk) {
7075 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7076 goto error;
7077 }
7078 chk->index = 0;
7079 LIST_ADDQ(&rs->rules, &chk->list);
7080
7081 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007082 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007083 if (!chk) {
7084 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7085 goto error;
7086 }
7087 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7088 chk->index = 1;
7089 LIST_ADDQ(&rs->rules, &chk->list);
7090
Christopher Fauletd7cee712020-04-21 13:45:00 +02007091 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007092
7093 ruleset_found:
7094 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007095 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007096
7097 out:
7098 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007099 free(errmsg);
7100 return err_code;
7101
7102 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007103 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007104 err_code |= ERR_ALERT | ERR_FATAL;
7105 goto out;
7106}
Christopher Fauletce355072020-04-02 11:44:39 +02007107
Christopher Faulete5870d82020-04-15 11:32:03 +02007108
7109struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7110{
7111 struct tcpcheck_rule *chk = NULL;
7112 struct tcpcheck_http_hdr *hdr = NULL;
7113 char *meth = NULL, *uri = NULL, *vsn = NULL;
7114 char *hdrs, *body;
7115
7116 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7117 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7118 if (hdrs == body)
7119 hdrs = NULL;
7120 if (hdrs) {
7121 *hdrs = '\0';
7122 hdrs +=2;
7123 }
7124 if (body) {
7125 *body = '\0';
7126 body += 4;
7127 }
7128 if (hdrs || body) {
7129 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7130 " Please, consider to use 'http-check send' directive instead.");
7131 }
7132
7133 chk = calloc(1, sizeof(*chk));
7134 if (!chk) {
7135 memprintf(errmsg, "out of memory");
7136 goto error;
7137 }
7138 chk->action = TCPCHK_ACT_SEND;
7139 chk->send.type = TCPCHK_SEND_HTTP;
7140 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7141 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7142 LIST_INIT(&chk->send.http.hdrs);
7143
7144 /* Copy the method, uri and version */
7145 if (*args[cur_arg]) {
7146 if (!*args[cur_arg+1])
7147 uri = args[cur_arg];
7148 else
7149 meth = args[cur_arg];
7150 }
7151 if (*args[cur_arg+1])
7152 uri = args[cur_arg+1];
7153 if (*args[cur_arg+2])
7154 vsn = args[cur_arg+2];
7155
7156 if (meth) {
7157 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7158 chk->send.http.meth.str.area = strdup(meth);
7159 chk->send.http.meth.str.data = strlen(meth);
7160 if (!chk->send.http.meth.str.area) {
7161 memprintf(errmsg, "out of memory");
7162 goto error;
7163 }
7164 }
7165 if (uri) {
7166 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007167 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007168 memprintf(errmsg, "out of memory");
7169 goto error;
7170 }
7171 }
7172 if (vsn) {
7173 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007174 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007175 memprintf(errmsg, "out of memory");
7176 goto error;
7177 }
7178 }
7179
7180 /* Copy the header */
7181 if (hdrs) {
7182 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7183 struct h1m h1m;
7184 int i, ret;
7185
7186 /* Build and parse the request */
7187 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7188
7189 h1m.flags = H1_MF_HDRS_ONLY;
7190 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7191 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7192 &h1m, NULL);
7193 if (ret <= 0) {
7194 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7195 goto error;
7196 }
7197
Christopher Fauletb61caf42020-04-21 10:57:42 +02007198 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007199 hdr = calloc(1, sizeof(*hdr));
7200 if (!hdr) {
7201 memprintf(errmsg, "out of memory");
7202 goto error;
7203 }
7204 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007205 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007206 if (!hdr->name.ptr) {
7207 memprintf(errmsg, "out of memory");
7208 goto error;
7209 }
7210
Christopher Fauletb61caf42020-04-21 10:57:42 +02007211 ist0(tmp_hdrs[i].v);
7212 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 +02007213 goto error;
7214 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7215 }
7216 }
7217
7218 /* Copy the body */
7219 if (body) {
7220 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007221 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007222 memprintf(errmsg, "out of memory");
7223 goto error;
7224 }
7225 }
7226
7227 return chk;
7228
7229 error:
7230 free_tcpcheck_http_hdr(hdr);
7231 free_tcpcheck(chk, 0);
7232 return NULL;
7233}
7234
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007235int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7236 const char *file, int line)
7237{
Christopher Faulete5870d82020-04-15 11:32:03 +02007238 struct tcpcheck_ruleset *rs = NULL;
7239 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7240 struct tcpcheck_rule *chk;
7241 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007242 int err_code = 0;
7243
7244 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7245 err_code |= ERR_WARN;
7246
7247 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7248 goto out;
7249
Christopher Faulete5870d82020-04-15 11:32:03 +02007250 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7251 if (!chk) {
7252 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7253 goto error;
7254 }
7255 if (errmsg) {
7256 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7257 err_code |= ERR_WARN;
7258 free(errmsg);
7259 errmsg = NULL;
7260 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007261
Christopher Faulete5870d82020-04-15 11:32:03 +02007262 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007263 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007264 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007265
Christopher Faulete5870d82020-04-15 11:32:03 +02007266 free_tcpcheck_vars(&rules->preset_vars);
7267 rules->list = NULL;
7268 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007269
Christopher Faulete5870d82020-04-15 11:32:03 +02007270 /* Deduce the ruleset name from the proxy info */
7271 chunk_printf(&trash, "*http-check-%s_%s-%d",
7272 ((curpx == defpx) ? "defaults" : curpx->id),
7273 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007274
Christopher Faulet61cc8522020-04-20 14:54:42 +02007275 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007276 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007277 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007278 if (rs == NULL) {
7279 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7280 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007281 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007282 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007283
Christopher Faulete5870d82020-04-15 11:32:03 +02007284 rules->list = &rs->rules;
7285 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7286 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7287 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7288 rules->list = NULL;
7289 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007290 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007291
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007292 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007293 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007294 return err_code;
7295
7296 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007297 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007298 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007299 err_code |= ERR_ALERT | ERR_FATAL;
7300 goto out;
7301}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007302
Christopher Faulet6f557912020-04-09 15:58:50 +02007303int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7304 const char *file, int line)
7305{
7306 int err_code = 0;
7307
Christopher Faulet6f557912020-04-09 15:58:50 +02007308 curpx->options2 &= ~PR_O2_CHK_ANY;
7309 curpx->options2 |= PR_O2_EXT_CHK;
7310 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7311 goto out;
7312
7313 out:
7314 return err_code;
7315}
7316
Christopher Fauletce8111e2020-04-06 15:04:11 +02007317/* Parse the "addr" server keyword */
7318static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7319 char **errmsg)
7320{
7321 struct sockaddr_storage *sk;
7322 struct protocol *proto;
7323 int port1, port2, err_code = 0;
7324
7325
7326 if (!*args[*cur_arg+1]) {
7327 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7328 goto error;
7329 }
7330
7331 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7332 if (!sk) {
7333 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7334 goto error;
7335 }
7336
7337 proto = protocol_by_family(sk->ss_family);
7338 if (!proto || !proto->connect) {
7339 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7340 args[*cur_arg], args[*cur_arg+1]);
7341 goto error;
7342 }
7343
7344 if (port1 != port2) {
7345 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7346 args[*cur_arg], args[*cur_arg+1]);
7347 goto error;
7348 }
7349
7350 srv->check.addr = srv->agent.addr = *sk;
7351 srv->flags |= SRV_F_CHECKADDR;
7352 srv->flags |= SRV_F_AGENTADDR;
7353
7354 out:
7355 return err_code;
7356
7357 error:
7358 err_code |= ERR_ALERT | ERR_FATAL;
7359 goto out;
7360}
7361
7362
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007363/* Parse the "agent-addr" server keyword */
7364static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7365 char **errmsg)
7366{
7367 int err_code = 0;
7368
7369 if (!*(args[*cur_arg+1])) {
7370 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7371 goto error;
7372 }
7373 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7374 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7375 goto error;
7376 }
7377
7378 out:
7379 return err_code;
7380
7381 error:
7382 err_code |= ERR_ALERT | ERR_FATAL;
7383 goto out;
7384}
7385
7386/* Parse the "agent-check" server keyword */
7387static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7388 char **errmsg)
7389{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007390 struct tcpcheck_ruleset *rs = NULL;
7391 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7392 struct tcpcheck_rule *chk;
7393 int err_code = 0;
7394
7395 if (srv->do_agent)
7396 goto out;
7397
7398 if (!rules) {
7399 rules = calloc(1, sizeof(*rules));
7400 if (!rules) {
7401 memprintf(errmsg, "out of memory.");
7402 goto error;
7403 }
7404 LIST_INIT(&rules->preset_vars);
7405 srv->agent.tcpcheck_rules = rules;
7406 }
7407 rules->list = NULL;
7408 rules->flags = 0;
7409
Christopher Faulet61cc8522020-04-20 14:54:42 +02007410 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007411 if (rs)
7412 goto ruleset_found;
7413
Christopher Faulet61cc8522020-04-20 14:54:42 +02007414 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007415 if (rs == NULL) {
7416 memprintf(errmsg, "out of memory.");
7417 goto error;
7418 }
7419
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007420 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007421 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7422 if (!chk) {
7423 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7424 goto error;
7425 }
7426 chk->index = 0;
7427 LIST_ADDQ(&rs->rules, &chk->list);
7428
7429 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007430 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7431 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007432 if (!chk) {
7433 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7434 goto error;
7435 }
7436 chk->expect.custom = tcpcheck_agent_expect_reply;
7437 chk->index = 1;
7438 LIST_ADDQ(&rs->rules, &chk->list);
7439
Christopher Fauletd7cee712020-04-21 13:45:00 +02007440 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007441
7442 ruleset_found:
7443 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007444 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007445 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007446
7447 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007448 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007449
7450 error:
7451 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007452 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007453 err_code |= ERR_ALERT | ERR_FATAL;
7454 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007455}
7456
7457/* Parse the "agent-inter" server keyword */
7458static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7459 char **errmsg)
7460{
7461 const char *err = NULL;
7462 unsigned int delay;
7463 int err_code = 0;
7464
7465 if (!*(args[*cur_arg+1])) {
7466 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7467 goto error;
7468 }
7469
7470 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7471 if (err == PARSE_TIME_OVER) {
7472 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7473 args[*cur_arg+1], args[*cur_arg], srv->id);
7474 goto error;
7475 }
7476 else if (err == PARSE_TIME_UNDER) {
7477 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7478 args[*cur_arg+1], args[*cur_arg], srv->id);
7479 goto error;
7480 }
7481 else if (err) {
7482 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7483 *err, srv->id);
7484 goto error;
7485 }
7486 if (delay <= 0) {
7487 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7488 delay, args[*cur_arg], srv->id);
7489 goto error;
7490 }
7491 srv->agent.inter = delay;
7492
7493 out:
7494 return err_code;
7495
7496 error:
7497 err_code |= ERR_ALERT | ERR_FATAL;
7498 goto out;
7499}
7500
7501/* Parse the "agent-port" server keyword */
7502static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7503 char **errmsg)
7504{
7505 int err_code = 0;
7506
7507 if (!*(args[*cur_arg+1])) {
7508 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7509 goto error;
7510 }
7511
7512 global.maxsock++;
7513 srv->agent.port = atol(args[*cur_arg+1]);
7514
7515 out:
7516 return err_code;
7517
7518 error:
7519 err_code |= ERR_ALERT | ERR_FATAL;
7520 goto out;
7521}
7522
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007523int set_srv_agent_send(struct server *srv, const char *send)
7524{
7525 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7526 struct tcpcheck_var *var = NULL;
7527 char *str;
7528
7529 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007530 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007531 if (str == NULL || var == NULL)
7532 goto error;
7533
7534 free_tcpcheck_vars(&rules->preset_vars);
7535
7536 var->data.type = SMP_T_STR;
7537 var->data.u.str.area = str;
7538 var->data.u.str.data = strlen(str);
7539 LIST_INIT(&var->list);
7540 LIST_ADDQ(&rules->preset_vars, &var->list);
7541
7542 return 1;
7543
7544 error:
7545 free(str);
7546 free(var);
7547 return 0;
7548}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007549
7550/* Parse the "agent-send" server keyword */
7551static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7552 char **errmsg)
7553{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007554 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007555 int err_code = 0;
7556
7557 if (!*(args[*cur_arg+1])) {
7558 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7559 goto error;
7560 }
7561
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007562 if (!rules) {
7563 rules = calloc(1, sizeof(*rules));
7564 if (!rules) {
7565 memprintf(errmsg, "out of memory.");
7566 goto error;
7567 }
7568 LIST_INIT(&rules->preset_vars);
7569 srv->agent.tcpcheck_rules = rules;
7570 }
7571
7572 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007573 memprintf(errmsg, "out of memory.");
7574 goto error;
7575 }
7576
7577 out:
7578 return err_code;
7579
7580 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007581 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007582 err_code |= ERR_ALERT | ERR_FATAL;
7583 goto out;
7584}
7585
7586/* Parse the "no-agent-send" server keyword */
7587static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7588 char **errmsg)
7589{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007590 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007591 return 0;
7592}
7593
Christopher Fauletce8111e2020-04-06 15:04:11 +02007594/* Parse the "check" server keyword */
7595static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7596 char **errmsg)
7597{
7598 srv->do_check = 1;
7599 return 0;
7600}
7601
7602/* Parse the "check-send-proxy" server keyword */
7603static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7604 char **errmsg)
7605{
7606 srv->check.send_proxy = 1;
7607 return 0;
7608}
7609
7610/* Parse the "check-via-socks4" server keyword */
7611static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7612 char **errmsg)
7613{
7614 srv->check.via_socks4 = 1;
7615 return 0;
7616}
7617
7618/* Parse the "no-check" server keyword */
7619static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7620 char **errmsg)
7621{
7622 deinit_srv_check(srv);
7623 return 0;
7624}
7625
7626/* Parse the "no-check-send-proxy" server keyword */
7627static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7628 char **errmsg)
7629{
7630 srv->check.send_proxy = 0;
7631 return 0;
7632}
7633
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007634/* parse the "check-proto" server keyword */
7635static int srv_parse_check_proto(char **args, int *cur_arg,
7636 struct proxy *px, struct server *newsrv, char **err)
7637{
7638 int err_code = 0;
7639
7640 if (!*args[*cur_arg + 1]) {
7641 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7642 goto error;
7643 }
7644 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7645 if (!newsrv->check.mux_proto) {
7646 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7647 goto error;
7648 }
7649
7650 out:
7651 return err_code;
7652
7653 error:
7654 err_code |= ERR_ALERT | ERR_FATAL;
7655 goto out;
7656}
7657
7658
Christopher Fauletce8111e2020-04-06 15:04:11 +02007659/* Parse the "rise" server keyword */
7660static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7661 char **errmsg)
7662{
7663 int err_code = 0;
7664
7665 if (!*args[*cur_arg + 1]) {
7666 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7667 goto error;
7668 }
7669
7670 srv->check.rise = atol(args[*cur_arg+1]);
7671 if (srv->check.rise <= 0) {
7672 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7673 goto error;
7674 }
7675
7676 if (srv->check.health)
7677 srv->check.health = srv->check.rise;
7678
7679 out:
7680 return err_code;
7681
7682 error:
7683 deinit_srv_agent_check(srv);
7684 err_code |= ERR_ALERT | ERR_FATAL;
7685 goto out;
7686 return 0;
7687}
7688
7689/* Parse the "fall" server keyword */
7690static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7691 char **errmsg)
7692{
7693 int err_code = 0;
7694
7695 if (!*args[*cur_arg + 1]) {
7696 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7697 goto error;
7698 }
7699
7700 srv->check.fall = atol(args[*cur_arg+1]);
7701 if (srv->check.fall <= 0) {
7702 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7703 goto error;
7704 }
7705
7706 out:
7707 return err_code;
7708
7709 error:
7710 deinit_srv_agent_check(srv);
7711 err_code |= ERR_ALERT | ERR_FATAL;
7712 goto out;
7713 return 0;
7714}
7715
7716/* Parse the "inter" server keyword */
7717static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7718 char **errmsg)
7719{
7720 const char *err = NULL;
7721 unsigned int delay;
7722 int err_code = 0;
7723
7724 if (!*(args[*cur_arg+1])) {
7725 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7726 goto error;
7727 }
7728
7729 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7730 if (err == PARSE_TIME_OVER) {
7731 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7732 args[*cur_arg+1], args[*cur_arg], srv->id);
7733 goto error;
7734 }
7735 else if (err == PARSE_TIME_UNDER) {
7736 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7737 args[*cur_arg+1], args[*cur_arg], srv->id);
7738 goto error;
7739 }
7740 else if (err) {
7741 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7742 *err, srv->id);
7743 goto error;
7744 }
7745 if (delay <= 0) {
7746 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7747 delay, args[*cur_arg], srv->id);
7748 goto error;
7749 }
7750 srv->check.inter = delay;
7751
7752 out:
7753 return err_code;
7754
7755 error:
7756 err_code |= ERR_ALERT | ERR_FATAL;
7757 goto out;
7758}
7759
7760
7761/* Parse the "fastinter" server keyword */
7762static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7763 char **errmsg)
7764{
7765 const char *err = NULL;
7766 unsigned int delay;
7767 int err_code = 0;
7768
7769 if (!*(args[*cur_arg+1])) {
7770 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7771 goto error;
7772 }
7773
7774 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7775 if (err == PARSE_TIME_OVER) {
7776 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7777 args[*cur_arg+1], args[*cur_arg], srv->id);
7778 goto error;
7779 }
7780 else if (err == PARSE_TIME_UNDER) {
7781 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7782 args[*cur_arg+1], args[*cur_arg], srv->id);
7783 goto error;
7784 }
7785 else if (err) {
7786 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7787 *err, srv->id);
7788 goto error;
7789 }
7790 if (delay <= 0) {
7791 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7792 delay, args[*cur_arg], srv->id);
7793 goto error;
7794 }
7795 srv->check.fastinter = delay;
7796
7797 out:
7798 return err_code;
7799
7800 error:
7801 err_code |= ERR_ALERT | ERR_FATAL;
7802 goto out;
7803}
7804
7805
7806/* Parse the "downinter" server keyword */
7807static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7808 char **errmsg)
7809{
7810 const char *err = NULL;
7811 unsigned int delay;
7812 int err_code = 0;
7813
7814 if (!*(args[*cur_arg+1])) {
7815 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7816 goto error;
7817 }
7818
7819 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7820 if (err == PARSE_TIME_OVER) {
7821 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7822 args[*cur_arg+1], args[*cur_arg], srv->id);
7823 goto error;
7824 }
7825 else if (err == PARSE_TIME_UNDER) {
7826 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7827 args[*cur_arg+1], args[*cur_arg], srv->id);
7828 goto error;
7829 }
7830 else if (err) {
7831 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7832 *err, srv->id);
7833 goto error;
7834 }
7835 if (delay <= 0) {
7836 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7837 delay, args[*cur_arg], srv->id);
7838 goto error;
7839 }
7840 srv->check.downinter = delay;
7841
7842 out:
7843 return err_code;
7844
7845 error:
7846 err_code |= ERR_ALERT | ERR_FATAL;
7847 goto out;
7848}
7849
7850/* Parse the "port" server keyword */
7851static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7852 char **errmsg)
7853{
7854 int err_code = 0;
7855
7856 if (!*(args[*cur_arg+1])) {
7857 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7858 goto error;
7859 }
7860
7861 global.maxsock++;
7862 srv->check.port = atol(args[*cur_arg+1]);
7863 srv->flags |= SRV_F_CHECKPORT;
7864
7865 out:
7866 return err_code;
7867
7868 error:
7869 err_code |= ERR_ALERT | ERR_FATAL;
7870 goto out;
7871}
7872
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007873static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007874 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7875 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7876 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007877 { 0, NULL, NULL },
7878}};
7879
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007880static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007881 { "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 +02007882 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7883 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7884 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7885 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7886 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007887 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007888 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007889 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7890 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007891 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007892 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7893 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7894 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7895 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7896 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7897 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7898 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7899 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007900 { NULL, NULL, 0 },
7901}};
7902
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007903INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007904INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007905
Willy Tarreaubd741542010-03-16 18:46:54 +01007906/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007907 * Local variables:
7908 * c-indent-level: 8
7909 * c-basic-offset: 8
7910 * End:
7911 */