blob: bb78cc845f1e40b78853c298a32edb0dd5acaf6a [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Willy Tarreau9b39dc52014-07-08 00:54:10 +020018#include <signal.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090019#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020020#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020021#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020023#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020024#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010025#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040027#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090028#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020030#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031#include <arpa/inet.h>
32
Christopher Fauletfd6c2292020-03-25 18:20:15 +010033#include <common/cfgparse.h>
Willy Tarreauc7e42382012-08-24 19:22:53 +020034#include <common/chunk.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020035#include <common/compat.h>
36#include <common/config.h>
37#include <common/mini-clist.h>
Willy Tarreau83749182007-04-15 20:56:27 +020038#include <common/standard.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020039#include <common/time.h>
Christopher Fauletcfda8472017-10-20 15:40:23 +020040#include <common/hathreads.h>
Christopher Faulete5870d82020-04-15 11:32:03 +020041#include <common/http.h>
42#include <common/h1.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020043#include <common/htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020044
45#include <types/global.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020046#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010047#include <types/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020048
Gaetan Rivet707b52f2020-02-21 18:14:59 +010049#include <proto/action.h>
Christopher Fauletba3c68f2020-04-01 16:27:05 +020050#include <proto/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020051#include <proto/backend.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020052#include <proto/checks.h>
William Lallemand9ed62032016-11-21 17:49:11 +010053#include <proto/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020054#include <proto/fd.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020055#include <proto/http_htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020056#include <proto/log.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020057#include <proto/mux_pt.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020058#include <proto/queue.h>
Willy Tarreauc6f4ce82009-06-10 11:09:37 +020059#include <proto/port_range.h>
Willy Tarreaue8c66af2008-01-13 18:40:14 +010060#include <proto/proto_tcp.h>
Baptiste Assmann69e273f2013-12-11 00:52:19 +010061#include <proto/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010062#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020063#include <proto/server.h>
Willy Tarreau48d6bf22016-06-21 16:27:34 +020064#include <proto/signal.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010065#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020066#include <proto/task.h>
Gaetan Rivet13a50432020-02-21 18:13:44 +010067#include <proto/vars.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020068#include <proto/log.h>
69#include <proto/dns.h>
70#include <proto/proto_udp.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020071#include <proto/ssl_sock.h>
Christopher Fauletb7d30092020-03-30 15:19:03 +020072#include <proto/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020073
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020074static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010075
Christopher Faulet61cc8522020-04-20 14:54:42 +020076static int wake_srv_chk(struct conn_stream *cs);
77struct data_cb check_conn_cb = {
78 .wake = wake_srv_chk,
79 .name = "CHCK",
80};
Christopher Fauletd7e63962020-04-17 20:15:59 +020081
Christopher Fauletd7cee712020-04-21 13:45:00 +020082/* Global tree to share all tcp-checks */
83struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020084
85
Willy Tarreau8ceae722018-11-26 11:58:30 +010086DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
87DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020088
Gaetan Rivet05d692d2020-02-14 17:42:54 +010089/* Dummy frontend used to create all checks sessions. */
90static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020091
Christopher Faulet61cc8522020-04-20 14:54:42 +020092/**************************************************************************/
93/************************ Handle check results ****************************/
94/**************************************************************************/
95struct check_status {
96 short result; /* one of SRV_CHK_* */
97 char *info; /* human readable short info */
98 char *desc; /* long description */
99};
100
101struct analyze_status {
102 char *desc; /* description */
103 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
104};
105
Simon Horman63a4a822012-03-19 07:24:41 +0900106static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100107 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
108 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200109 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200110
Willy Tarreau23964182014-05-20 20:56:30 +0200111 /* Below we have finished checks */
112 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100114
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100115 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100117 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
118 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
119 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200120
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100121 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
122 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
123 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200124
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100125 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
126 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200127
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200128 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200129
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100130 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
131 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
132 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900133
134 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
135 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200136 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200137};
138
Simon Horman63a4a822012-03-19 07:24:41 +0900139static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100140 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
141
142 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
143 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
144
145 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
146 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
147 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
148 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
149
150 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
151 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
152 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
153};
154
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100155/* checks if <err> is a real error for errno or one that can be ignored, and
156 * return 0 for these ones or <err> for real ones.
157 */
158static inline int unclean_errno(int err)
159{
160 if (err == EAGAIN || err == EINPROGRESS ||
161 err == EISCONN || err == EALREADY)
162 return 0;
163 return err;
164}
165
Christopher Faulet61cc8522020-04-20 14:54:42 +0200166/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200167const char *get_check_status_description(short check_status) {
168
169 const char *desc;
170
171 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200172 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200173 else
174 desc = NULL;
175
176 if (desc && *desc)
177 return desc;
178 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200179 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200180}
181
Christopher Faulet61cc8522020-04-20 14:54:42 +0200182/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200183const char *get_check_status_info(short check_status) {
184
185 const char *info;
186
187 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200188 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200189 else
190 info = NULL;
191
192 if (info && *info)
193 return info;
194 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200195 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200196}
197
Christopher Faulet61cc8522020-04-20 14:54:42 +0200198/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100199const char *get_analyze_status(short analyze_status) {
200
201 const char *desc;
202
203 if (analyze_status < HANA_STATUS_SIZE)
204 desc = analyze_statuses[analyze_status].desc;
205 else
206 desc = NULL;
207
208 if (desc && *desc)
209 return desc;
210 else
211 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
212}
213
Christopher Faulet61cc8522020-04-20 14:54:42 +0200214/* Sets check->status, update check->duration and fill check->result with an
215 * adequate CHK_RES_* value. The new check->health is computed based on the
216 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200217 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200218 * Shows information in logs about failed health check if server is UP or
219 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200220 */
Simon Horman4a741432013-02-23 15:35:38 +0900221static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100222{
Simon Horman4a741432013-02-23 15:35:38 +0900223 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200224 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200225 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900226
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200227 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100228 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900229 check->desc[0] = '\0';
230 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200231 return;
232 }
233
Simon Horman4a741432013-02-23 15:35:38 +0900234 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200235 return;
236
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200237 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900238 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
239 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200240 } else
Simon Horman4a741432013-02-23 15:35:38 +0900241 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200242
Simon Horman4a741432013-02-23 15:35:38 +0900243 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200244 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900245 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200246
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100247 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900248 check->duration = -1;
249 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200250 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900251 check->duration = tv_ms_elapsed(&check->start, &now);
252 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200253 }
254
Willy Tarreau23964182014-05-20 20:56:30 +0200255 /* no change is expected if no state change occurred */
256 if (check->result == CHK_RES_NEUTRAL)
257 return;
258
Olivier Houchard0923fa42019-01-11 18:43:04 +0100259 /* If the check was really just sending a mail, it won't have an
260 * associated server, so we're done now.
261 */
262 if (!s)
263 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200265
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200266 switch (check->result) {
267 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200268 /* Failure to connect to the agent as a secondary check should not
269 * cause the server to be marked down.
270 */
271 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900272 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200273 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100274 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200275 report = 1;
276 check->health--;
277 if (check->health < check->rise)
278 check->health = 0;
279 }
280 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200281
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200282 case CHK_RES_PASSED:
283 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
284 if ((check->health < check->rise + check->fall - 1) &&
285 (check->result == CHK_RES_PASSED || check->health > 0)) {
286 report = 1;
287 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200288
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200289 if (check->health >= check->rise)
290 check->health = check->rise + check->fall - 1; /* OK now */
291 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200292
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200293 /* clear consecutive_errors if observing is enabled */
294 if (s->onerror)
295 s->consecutive_errors = 0;
296 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100297
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200298 default:
299 break;
300 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200301
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200302 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
303 (status != prev_status || report)) {
304 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200305 "%s check for %sserver %s/%s %s%s",
306 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200307 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100308 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100309 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200310 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311
Emeric Brun5a133512017-10-19 14:42:30 +0200312 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200313
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100314 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200315 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
316 (check->health >= check->rise) ? check->fall : check->rise,
317 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200318
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200319 ha_warning("%s.\n", trash.area);
320 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
321 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200322 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200323}
324
Willy Tarreau4eec5472014-05-20 22:32:27 +0200325/* Marks the check <check>'s server down if the current check is already failed
326 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200327 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200328static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200329{
Simon Horman4a741432013-02-23 15:35:38 +0900330 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900331
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200332 /* The agent secondary check should only cause a server to be marked
333 * as down if check->status is HCHK_STATUS_L7STS, which indicates
334 * that the agent returned "fail", "stopped" or "down".
335 * The implication here is that failure to connect to the agent
336 * as a secondary check should not cause the server to be marked
337 * down. */
338 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
339 return;
340
Willy Tarreau4eec5472014-05-20 22:32:27 +0200341 if (check->health > 0)
342 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100343
Willy Tarreau4eec5472014-05-20 22:32:27 +0200344 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200345 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200346}
347
Willy Tarreauaf549582014-05-16 17:37:50 +0200348/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200349 * it isn't in maintenance, it is not tracking a down server and other checks
350 * comply. The rule is simple : by default, a server is up, unless any of the
351 * following conditions is true :
352 * - health check failed (check->health < rise)
353 * - agent check failed (agent->health < rise)
354 * - the server tracks a down server (track && track->state == STOPPED)
355 * Note that if the server has a slowstart, it will switch to STARTING instead
356 * of RUNNING. Also, only the health checks support the nolb mode, so the
357 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200358 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200359static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200360{
Simon Horman4a741432013-02-23 15:35:38 +0900361 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100362
Emeric Brun52a91d32017-08-31 14:41:55 +0200363 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200364 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100365
Emeric Brun52a91d32017-08-31 14:41:55 +0200366 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200367 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368
Willy Tarreau3e048382014-05-21 10:30:54 +0200369 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
370 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100371
Willy Tarreau3e048382014-05-21 10:30:54 +0200372 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
373 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200374
Emeric Brun52a91d32017-08-31 14:41:55 +0200375 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200376 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100377
Emeric Brun5a133512017-10-19 14:42:30 +0200378 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100379}
380
Willy Tarreaudb58b792014-05-21 13:57:23 +0200381/* Marks the check <check> as valid and tries to set its server into stopping mode
382 * if it was running or starting, and provided it isn't in maintenance and other
383 * checks comply. The conditions for the server to be marked in stopping mode are
384 * the same as for it to be turned up. Also, only the health checks support the
385 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200386 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200387static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200388{
Simon Horman4a741432013-02-23 15:35:38 +0900389 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100390
Emeric Brun52a91d32017-08-31 14:41:55 +0200391 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200392 return;
393
Willy Tarreaudb58b792014-05-21 13:57:23 +0200394 if (check->state & CHK_ST_AGENT)
395 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396
Emeric Brun52a91d32017-08-31 14:41:55 +0200397 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200398 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100399
Willy Tarreaudb58b792014-05-21 13:57:23 +0200400 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
401 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100402
Willy Tarreaudb58b792014-05-21 13:57:23 +0200403 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
404 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100405
Willy Tarreaub26881a2017-12-23 11:16:49 +0100406 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100407}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200408
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100409/* note: use health_adjust() only, which first checks that the observe mode is
410 * enabled.
411 */
412void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100413{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100414 int failed;
415 int expire;
416
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100417 if (s->observe >= HANA_OBS_SIZE)
418 return;
419
Willy Tarreaubb956662013-01-24 00:37:39 +0100420 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100421 return;
422
423 switch (analyze_statuses[status].lr[s->observe - 1]) {
424 case 1:
425 failed = 1;
426 break;
427
428 case 2:
429 failed = 0;
430 break;
431
432 default:
433 return;
434 }
435
436 if (!failed) {
437 /* good: clear consecutive_errors */
438 s->consecutive_errors = 0;
439 return;
440 }
441
Olivier Houchard7059c552019-03-08 18:49:32 +0100442 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100443
444 if (s->consecutive_errors < s->consecutive_errors_limit)
445 return;
446
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100447 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
448 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100449
450 switch (s->onerror) {
451 case HANA_ONERR_FASTINTER:
452 /* force fastinter - nothing to do here as all modes force it */
453 break;
454
455 case HANA_ONERR_SUDDTH:
456 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900457 if (s->check.health > s->check.rise)
458 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100459
460 /* no break - fall through */
461
462 case HANA_ONERR_FAILCHK:
463 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200464 set_server_check_status(&s->check, HCHK_STATUS_HANA,
465 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200466 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100467 break;
468
469 case HANA_ONERR_MARKDWN:
470 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900471 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200472 set_server_check_status(&s->check, HCHK_STATUS_HANA,
473 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200474 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100475 break;
476
477 default:
478 /* write a warning? */
479 break;
480 }
481
482 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100483 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100484
Simon Horman66183002013-02-23 10:16:43 +0900485 if (s->check.fastinter) {
486 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300487 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200488 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300489 /* requeue check task with new expire */
490 task_queue(s->check.task);
491 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100492 }
Willy Tarreauef781042010-01-27 11:53:01 +0100493}
494
Christopher Faulet61cc8522020-04-20 14:54:42 +0200495/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100496 * closed, keep errno intact as it is supposed to contain the valid error code.
497 * If no error is reported, check the socket's error queue using getsockopt().
498 * Warning, this must be done only once when returning from poll, and never
499 * after an I/O error was attempted, otherwise the error queue might contain
500 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
501 * socket. Returns non-zero if an error was reported, zero if everything is
502 * clean (including a properly closed socket).
503 */
504static int retrieve_errno_from_socket(struct connection *conn)
505{
506 int skerr;
507 socklen_t lskerr = sizeof(skerr);
508
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100509 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100510 return 1;
511
Willy Tarreau3c728722014-01-23 13:50:42 +0100512 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100513 return 0;
514
Willy Tarreau585744b2017-08-24 14:31:19 +0200515 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100516 errno = skerr;
517
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100518 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100519
520 if (!errno) {
521 /* we could not retrieve an error, that does not mean there is
522 * none. Just don't change anything and only report the prior
523 * error if any.
524 */
525 if (conn->flags & CO_FL_ERROR)
526 return 1;
527 else
528 return 0;
529 }
530
531 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
532 return 1;
533}
534
Christopher Faulet61cc8522020-04-20 14:54:42 +0200535/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100536 * and adjust the server status accordingly. It may make use of <errno_bck>
537 * if non-null when the caller is absolutely certain of its validity (eg:
538 * checked just after a syscall). If the caller doesn't have a valid errno,
539 * it can pass zero, and retrieve_errno_from_socket() will be called to try
540 * to extract errno from the socket. If no error is reported, it will consider
541 * the <expired> flag. This is intended to be used when a connection error was
542 * reported in conn->flags or when a timeout was reported in <expired>. The
543 * function takes care of not updating a server status which was already set.
544 * All situations where at least one of <expired> or CO_FL_ERROR are set
545 * produce a status.
546 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200547static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200549 struct conn_stream *cs = check->cs;
550 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100551 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200552 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200553 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100555 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100556 return;
557
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100558 errno = unclean_errno(errno_bck);
559 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100560 retrieve_errno_from_socket(conn);
561
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200562 if (conn && !(conn->flags & CO_FL_ERROR) &&
563 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100564 return;
565
566 /* we'll try to build a meaningful error message depending on the
567 * context of the error possibly present in conn->err_code, and the
568 * socket error possibly collected above. This is useful to know the
569 * exact step of the L6 layer (eg: SSL handshake).
570 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200571 chk = get_trash_chunk();
572
Christopher Faulet799f3a42020-04-07 12:06:14 +0200573 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200574 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200575 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200576 if (!step)
577 chunk_printf(chk, " at initial connection step of tcp-check");
578 else {
579 chunk_printf(chk, " at step %d of tcp-check", step);
580 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200581 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
582 if (check->current_step->connect.port)
583 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200584 else
585 chunk_appendf(chk, " (connect)");
586 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200587 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
588 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100589
590 switch (expect->type) {
591 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200592 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100593 break;
594 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200595 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100596 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200597 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200598 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100599 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200600 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100601 chunk_appendf(chk, " (expect binary regex)");
602 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200603 case TCPCHK_EXPECT_STRING_LF:
604 chunk_appendf(chk, " (expect log-format string)");
605 break;
606 case TCPCHK_EXPECT_BINARY_LF:
607 chunk_appendf(chk, " (expect log-format binary)");
608 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200610 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200612 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200613 chunk_appendf(chk, " (expect HTTP status regex)");
614 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200615 case TCPCHK_EXPECT_HTTP_HEADER:
616 chunk_appendf(chk, " (expect HTTP header pattern)");
617 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200618 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200619 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200620 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200621 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200622 chunk_appendf(chk, " (expect HTTP body regex)");
623 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200624 case TCPCHK_EXPECT_HTTP_BODY_LF:
625 chunk_appendf(chk, " (expect log-format HTTP body)");
626 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200627 case TCPCHK_EXPECT_CUSTOM:
628 chunk_appendf(chk, " (expect custom function)");
629 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100630 case TCPCHK_EXPECT_UNDEF:
631 chunk_appendf(chk, " (undefined expect!)");
632 break;
633 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200634 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200635 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200636 chunk_appendf(chk, " (send)");
637 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200638
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200639 if (check->current_step && check->current_step->comment)
640 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200641 }
642 }
643
Willy Tarreau00149122017-10-04 18:05:01 +0200644 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100645 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200646 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
647 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100648 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
650 chk->area);
651 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100652 }
653 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100654 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200655 chunk_printf(&trash, "%s%s", strerror(errno),
656 chk->area);
657 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100658 }
659 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200660 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100661 }
662 }
663
Willy Tarreau00149122017-10-04 18:05:01 +0200664 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200665 /* NOTE: this is reported after <fall> tries */
666 chunk_printf(chk, "No port available for the TCP connection");
667 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
668 }
669
Willy Tarreau00149122017-10-04 18:05:01 +0200670 if (!conn) {
671 /* connection allocation error before the connection was established */
672 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
673 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100674 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100675 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200676 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100677 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
678 else if (expired)
679 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200680
681 /*
682 * might be due to a server IP change.
683 * Let's trigger a DNS resolution if none are currently running.
684 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100685 if (check->server)
686 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200687
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100688 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100689 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100690 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200691 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100692 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
693 else if (expired)
694 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
695 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200696 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 /* I/O error after connection was established and before we could diagnose */
698 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
699 }
700 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200701 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
702
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100703 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200704 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
705 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200706 tout = check->current_step->expect.tout_status;
707 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100708 }
709
710 return;
711}
712
Willy Tarreaubaaee002006-06-26 02:48:02 +0200713
Christopher Faulet61cc8522020-04-20 14:54:42 +0200714/**************************************************************************/
715/*************** Init/deinit tcp-check rules and ruleset ******************/
716/**************************************************************************/
717/* Releases memory allocated for a log-format string */
718static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200719{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200720 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100721
Christopher Faulet61cc8522020-04-20 14:54:42 +0200722 list_for_each_entry_safe(lf, lfb, fmt, list) {
723 LIST_DEL(&lf->list);
724 release_sample_expr(lf->expr);
725 free(lf->arg);
726 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100727 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200728}
729
Christopher Faulet61cc8522020-04-20 14:54:42 +0200730/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
731static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100732{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200733 if (!hdr)
734 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200735
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200737 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200738 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100739}
740
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741/* Releases memory allocated for an HTTP header list used in a tcp-check send
742 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200743 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200744static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200745{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200746 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200747
Christopher Faulet61cc8522020-04-20 14:54:42 +0200748 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
749 LIST_DEL(&hdr->list);
750 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200751 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200752}
753
Christopher Faulet61cc8522020-04-20 14:54:42 +0200754/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
755 * tcp-check was allocated using a memory pool (it is used to instantiate email
756 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200757 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200758static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200759{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200760 if (!rule)
761 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200762
Christopher Faulet61cc8522020-04-20 14:54:42 +0200763 free(rule->comment);
764 switch (rule->action) {
765 case TCPCHK_ACT_SEND:
766 switch (rule->send.type) {
767 case TCPCHK_SEND_STRING:
768 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200769 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200770 break;
771 case TCPCHK_SEND_STRING_LF:
772 case TCPCHK_SEND_BINARY_LF:
773 free_tcpcheck_fmt(&rule->send.fmt);
774 break;
775 case TCPCHK_SEND_HTTP:
776 free(rule->send.http.meth.str.area);
777 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200778 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200779 else
780 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200781 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200782 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
783 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200784 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200785 else
786 free_tcpcheck_fmt(&rule->send.http.body_fmt);
787 break;
788 case TCPCHK_SEND_UNDEF:
789 break;
790 }
791 break;
792 case TCPCHK_ACT_EXPECT:
793 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
794 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
795 release_sample_expr(rule->expect.status_expr);
796 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200797 case TCPCHK_EXPECT_HTTP_STATUS:
798 free(rule->expect.codes.codes);
799 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200800 case TCPCHK_EXPECT_STRING:
801 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200802 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200803 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200804 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200805 case TCPCHK_EXPECT_STRING_REGEX:
806 case TCPCHK_EXPECT_BINARY_REGEX:
807 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
808 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200809 regex_free(rule->expect.regex);
810 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200811 case TCPCHK_EXPECT_STRING_LF:
812 case TCPCHK_EXPECT_BINARY_LF:
813 case TCPCHK_EXPECT_HTTP_BODY_LF:
814 free_tcpcheck_fmt(&rule->expect.fmt);
815 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200816 case TCPCHK_EXPECT_HTTP_HEADER:
817 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
818 regex_free(rule->expect.hdr.name_re);
819 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
820 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
821 else
822 istfree(&rule->expect.hdr.name);
823
824 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
825 regex_free(rule->expect.hdr.value_re);
826 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
827 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
828 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
829 istfree(&rule->expect.hdr.value);
830 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200831 case TCPCHK_EXPECT_CUSTOM:
832 case TCPCHK_EXPECT_UNDEF:
833 break;
834 }
835 break;
836 case TCPCHK_ACT_CONNECT:
837 free(rule->connect.sni);
838 free(rule->connect.alpn);
839 release_sample_expr(rule->connect.port_expr);
840 break;
841 case TCPCHK_ACT_COMMENT:
842 break;
843 case TCPCHK_ACT_ACTION_KW:
844 free(rule->action_kw.rule);
845 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200846 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200847
848 if (in_pool)
849 pool_free(pool_head_tcpcheck_rule, rule);
850 else
851 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200852}
853
Christopher Faulet61cc8522020-04-20 14:54:42 +0200854/* Creates a tcp-check variable used in preset variables before executing a
855 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100856 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200857static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100858{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200859 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100860
Christopher Faulet61cc8522020-04-20 14:54:42 +0200861 var = calloc(1, sizeof(*var));
862 if (var == NULL)
863 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100864
Christopher Fauletb61caf42020-04-21 10:57:42 +0200865 var->name = istdup(name);
866 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200867 free(var);
868 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100869 }
Simon Horman98637e52014-06-20 12:30:16 +0900870
Christopher Faulet61cc8522020-04-20 14:54:42 +0200871 LIST_INIT(&var->list);
872 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900873}
874
Christopher Faulet61cc8522020-04-20 14:54:42 +0200875/* Releases memory allocated for a preset tcp-check variable */
876static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900877{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200878 if (!var)
879 return;
880
Christopher Fauletb61caf42020-04-21 10:57:42 +0200881 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200882 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
883 free(var->data.u.str.area);
884 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
885 free(var->data.u.meth.str.area);
886 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900887}
888
Christopher Faulet61cc8522020-04-20 14:54:42 +0200889/* Releases a list of preset tcp-check variables */
890static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900891{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200892 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200893
Christopher Faulet61cc8522020-04-20 14:54:42 +0200894 list_for_each_entry_safe(var, back, vars, list) {
895 LIST_DEL(&var->list);
896 free_tcpcheck_var(var);
897 }
Simon Horman98637e52014-06-20 12:30:16 +0900898}
899
Christopher Faulet61cc8522020-04-20 14:54:42 +0200900/* Duplicate a list of preset tcp-check variables */
901int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900902{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900904
Christopher Faulet61cc8522020-04-20 14:54:42 +0200905 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200906 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200907 if (!new)
908 goto error;
909 new->data.type = var->data.type;
910 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
911 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
912 goto error;
913 if (var->data.type == SMP_T_STR)
914 new->data.u.str.area[new->data.u.str.data] = 0;
915 }
916 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
917 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
918 goto error;
919 new->data.u.str.area[new->data.u.str.data] = 0;
920 new->data.u.meth.meth = var->data.u.meth.meth;
921 }
922 else
923 new->data.u = var->data.u;
924 LIST_ADDQ(dst, &new->list);
925 }
926 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900927
Christopher Faulet61cc8522020-04-20 14:54:42 +0200928 error:
929 free(new);
930 return 0;
931}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200932
Christopher Faulet61cc8522020-04-20 14:54:42 +0200933/* Looks for a shared tcp-check ruleset given its name. */
934static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
935{
936 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200937 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900938
Christopher Fauletd7cee712020-04-21 13:45:00 +0200939 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
940 if (node) {
941 rs = container_of(node, typeof(*rs), node);
942 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200943 }
944 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900945}
946
Christopher Faulet61cc8522020-04-20 14:54:42 +0200947/* Creates a new shared tcp-check ruleset */
948static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900949{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900951
Christopher Faulet61cc8522020-04-20 14:54:42 +0200952 rs = calloc(1, sizeof(*rs));
953 if (rs == NULL)
954 return NULL;
955
Christopher Fauletd7cee712020-04-21 13:45:00 +0200956 rs->node.key = strdup(name);
957 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200958 free(rs);
959 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900960 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200963 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900965}
966
Christopher Faulet61cc8522020-04-20 14:54:42 +0200967/* Releases memory allocated by a tcp-check ruleset. */
968static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900969{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200970 struct tcpcheck_rule *r, *rb;
971 if (!rs)
972 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200973
Christopher Fauletd7cee712020-04-21 13:45:00 +0200974 ebpt_delete(&rs->node);
975 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200976 list_for_each_entry_safe(r, rb, &rs->rules, list) {
977 LIST_DEL(&r->list);
978 free_tcpcheck(r, 0);
979 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200980 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900981}
982
Christopher Faulet61cc8522020-04-20 14:54:42 +0200983
984/**************************************************************************/
985/**************** Everything about tcp-checks execution *******************/
986/**************************************************************************/
987/* Returns the id of a step in a tcp-check ruleset */
988static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200989{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200990 if (!rule)
991 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900992
Christopher Faulet61cc8522020-04-20 14:54:42 +0200993 /* no last started step => first step */
994 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900995 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900996
Christopher Faulet61cc8522020-04-20 14:54:42 +0200997 /* last step is the first implicit connect */
998 if (rule->index == 0 &&
999 rule->action == TCPCHK_ACT_CONNECT &&
1000 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1001 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001002
Christopher Faulet61cc8522020-04-20 14:54:42 +02001003 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001004}
1005
Christopher Faulet61cc8522020-04-20 14:54:42 +02001006/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1007 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001008 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001010{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001011 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001012
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 list_for_each_entry(r, rules->list, list) {
1014 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1015 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001016 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001017 return NULL;
1018}
Cyril Bontéac92a062014-12-27 22:28:38 +01001019
Christopher Faulet61cc8522020-04-20 14:54:42 +02001020/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1021 * NULL if none was found.
1022 */
1023static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1024{
1025 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001026
Christopher Faulet61cc8522020-04-20 14:54:42 +02001027 list_for_each_entry_rev(r, rules->list, list) {
1028 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1029 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001030 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001031 return NULL;
1032}
Cyril Bontéac92a062014-12-27 22:28:38 +01001033
Christopher Faulet61cc8522020-04-20 14:54:42 +02001034/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1035 * <start> or NULL if non was found. If <start> is NULL, it relies on
1036 * get_first_tcpcheck_rule().
1037 */
1038static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1039{
1040 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001041
Christopher Faulet61cc8522020-04-20 14:54:42 +02001042 if (!start)
1043 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001044
Christopher Faulet61cc8522020-04-20 14:54:42 +02001045 r = LIST_NEXT(&start->list, typeof(r), list);
1046 list_for_each_entry_from(r, rules->list, list) {
1047 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1048 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001049 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001051}
Simon Horman98637e52014-06-20 12:30:16 +09001052
Simon Horman98637e52014-06-20 12:30:16 +09001053
Christopher Faulet61cc8522020-04-20 14:54:42 +02001054/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1055static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1056 int match, struct ist info)
1057{
1058 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001059
Christopher Faulet61cc8522020-04-20 14:54:42 +02001060 /* Follows these step to produce the info message:
1061 * 1. if info field is already provided, copy it
1062 * 2. if the expect rule provides an onerror log-format string,
1063 * use it to produce the message
1064 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1065 * 4. Otherwise produce the generic tcp-check info message
1066 */
1067 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001068 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001070 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001071 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1072 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1073 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001074 }
Simon Horman98637e52014-06-20 12:30:16 +09001075
Christopher Faulet61cc8522020-04-20 14:54:42 +02001076 if (check->type == PR_O2_TCPCHK_CHK &&
1077 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1078 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001079
Christopher Faulet61cc8522020-04-20 14:54:42 +02001080 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1081 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001082 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001083 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1084 break;
1085 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001087 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001088 tcpcheck_get_step_id(check, rule));
1089 break;
1090 case TCPCHK_EXPECT_BINARY:
1091 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1092 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001093 case TCPCHK_EXPECT_STRING_REGEX:
1094 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1095 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001096 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1097 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001098 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001100 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001101 case TCPCHK_EXPECT_STRING_LF:
1102 case TCPCHK_EXPECT_HTTP_BODY_LF:
1103 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1104 break;
1105 case TCPCHK_EXPECT_BINARY_LF:
1106 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1107 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001108 case TCPCHK_EXPECT_CUSTOM:
1109 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1110 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001111 case TCPCHK_EXPECT_HTTP_HEADER:
1112 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001113 case TCPCHK_EXPECT_UNDEF:
1114 /* Should never happen. */
1115 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001116 }
1117
Christopher Faulet61cc8522020-04-20 14:54:42 +02001118 comment:
1119 /* If the failing expect rule provides a comment, it is concatenated to
1120 * the info message.
1121 */
1122 if (rule->comment) {
1123 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001124 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001125 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001126
Christopher Faulet61cc8522020-04-20 14:54:42 +02001127 /* Finally, the check status code is set if the failing expect rule
1128 * defines a status expression.
1129 */
1130 if (rule->expect.status_expr) {
1131 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001132 rule->expect.status_expr, SMP_T_STR);
1133
1134 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1135 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001137 }
Simon Horman98637e52014-06-20 12:30:16 +09001138
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 *(b_tail(msg)) = '\0';
1140}
Cyril Bontéac92a062014-12-27 22:28:38 +01001141
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1143static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1144 struct ist info)
1145{
1146 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001147
Christopher Faulet61cc8522020-04-20 14:54:42 +02001148 /* Follows these step to produce the info message:
1149 * 1. if info field is already provided, copy it
1150 * 2. if the expect rule provides an onsucces log-format string,
1151 * use it to produce the message
1152 * 3. the expect rule is part of a protcol check (http, redis, mysql...), do nothing
1153 * 4. Otherwise produce the generic tcp-check info message
1154 */
1155 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001156 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001157 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1158 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1159 &rule->expect.onsuccess_fmt);
1160 else if (check->type == PR_O2_TCPCHK_CHK &&
1161 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1162 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001163
Christopher Faulet61cc8522020-04-20 14:54:42 +02001164 /* Finally, the check status code is set if the expect rule defines a
1165 * status expression.
1166 */
1167 if (rule->expect.status_expr) {
1168 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001169 rule->expect.status_expr, SMP_T_STR);
1170
1171 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1172 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001173 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001174 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001175
1176 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001177}
1178
Christopher Faulet61cc8522020-04-20 14:54:42 +02001179/* Builds the server state header used by HTTP health-checks */
1180static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001181{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001182 int sv_state;
1183 int ratio;
1184 char addr[46];
1185 char port[6];
1186 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1187 "UP %d/%d", "UP",
1188 "NOLB %d/%d", "NOLB",
1189 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001190
Christopher Faulet61cc8522020-04-20 14:54:42 +02001191 if (!(s->check.state & CHK_ST_ENABLED))
1192 sv_state = 6;
1193 else if (s->cur_state != SRV_ST_STOPPED) {
1194 if (s->check.health == s->check.rise + s->check.fall - 1)
1195 sv_state = 3; /* UP */
1196 else
1197 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001198
Christopher Faulet61cc8522020-04-20 14:54:42 +02001199 if (s->cur_state == SRV_ST_STOPPING)
1200 sv_state += 2;
1201 } else {
1202 if (s->check.health)
1203 sv_state = 1; /* going up */
1204 else
1205 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001206 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001207
Christopher Faulet61cc8522020-04-20 14:54:42 +02001208 chunk_appendf(buf, srv_hlt_st[sv_state],
1209 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1210 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001211
Christopher Faulet61cc8522020-04-20 14:54:42 +02001212 addr_to_str(&s->addr, addr, sizeof(addr));
1213 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1214 snprintf(port, sizeof(port), "%u", s->svc_port);
1215 else
1216 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001217
Christopher Faulet61cc8522020-04-20 14:54:42 +02001218 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1219 addr, port, s->proxy->id, s->id,
1220 global.node,
1221 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1222 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1223 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1224 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001225
Christopher Faulet61cc8522020-04-20 14:54:42 +02001226 if ((s->cur_state == SRV_ST_STARTING) &&
1227 now.tv_sec < s->last_change + s->slowstart &&
1228 now.tv_sec >= s->last_change) {
1229 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1230 chunk_appendf(buf, "; throttle=%d%%", ratio);
1231 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 return b_data(buf);
1234}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236/* Internal functions to parse and validate a MySQL packet in the context of an
1237 * expect rule. It start to parse the input buffer at the offset <offset>. If
1238 * <last_read> is set, no more data are expected.
1239 */
1240static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1241 unsigned int offset, int last_read)
1242{
1243 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1244 enum healthcheck_status status;
1245 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001246 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001247 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001248
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001249
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001251 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001252 if (!last_read)
1253 goto wait_more_data;
1254
1255 /* invalid length or truncated response */
1256 status = HCHK_STATUS_L7RSP;
1257 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001258 }
1259
Christopher Faulet61cc8522020-04-20 14:54:42 +02001260 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1261 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1262 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001263
Christopher Faulet61cc8522020-04-20 14:54:42 +02001264 if (b_data(&check->bi) < offset+plen+4) {
1265 if (!last_read)
1266 goto wait_more_data;
1267
1268 /* invalid length or truncated response */
1269 status = HCHK_STATUS_L7RSP;
1270 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001271 }
Simon Horman98637e52014-06-20 12:30:16 +09001272
Christopher Faulet61cc8522020-04-20 14:54:42 +02001273 if (*b_peek(&check->bi, offset+4) == '\xff') {
1274 /* MySQL Error packet always begin with field_count = 0xff */
1275 status = HCHK_STATUS_L7STS;
1276 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1277 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1278 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1279 goto error;
1280 }
Simon Horman98637e52014-06-20 12:30:16 +09001281
Christopher Faulet61cc8522020-04-20 14:54:42 +02001282 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1283 /* Not the last rule, continue */
1284 goto out;
1285 }
Simon Horman98637e52014-06-20 12:30:16 +09001286
Christopher Faulet61cc8522020-04-20 14:54:42 +02001287 /* We set the MySQL Version in description for information purpose
1288 * FIXME : it can be cool to use MySQL Version for other purpose,
1289 * like mark as down old MySQL server.
1290 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001291 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1292 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001293
Christopher Faulet61cc8522020-04-20 14:54:42 +02001294 out:
1295 free_trash_chunk(msg);
1296 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001297
Christopher Faulet61cc8522020-04-20 14:54:42 +02001298 error:
1299 ret = TCPCHK_EVAL_STOP;
1300 check->code = err;
1301 msg = alloc_trash_chunk();
1302 if (msg)
1303 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1304 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1305 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001306
Christopher Faulet61cc8522020-04-20 14:54:42 +02001307 wait_more_data:
1308 ret = TCPCHK_EVAL_WAIT;
1309 goto out;
1310}
Simon Horman98637e52014-06-20 12:30:16 +09001311
Christopher Faulet61cc8522020-04-20 14:54:42 +02001312/* Custom tcp-check expect function to parse and validate the MySQL initial
1313 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1314 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1315 * error occurred.
1316 */
1317static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1318{
1319 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1320}
Simon Horman98637e52014-06-20 12:30:16 +09001321
Christopher Faulet61cc8522020-04-20 14:54:42 +02001322/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1323 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1324 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1325 * an error occurred.
1326 */
1327static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1328{
1329 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001330
Christopher Faulet61cc8522020-04-20 14:54:42 +02001331 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1332 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1333 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1336}
Simon Horman98637e52014-06-20 12:30:16 +09001337
Christopher Faulet61cc8522020-04-20 14:54:42 +02001338/* Custom tcp-check expect function to parse and validate the LDAP bind response
1339 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1340 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1341 * error occurred.
1342 */
1343static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1344{
1345 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1346 enum healthcheck_status status;
1347 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001348 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001350
Christopher Faulet61cc8522020-04-20 14:54:42 +02001351 /* Check if the server speaks LDAP (ASN.1/BER)
1352 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1353 * http://tools.ietf.org/html/rfc4511
1354 */
1355 /* size of LDAPMessage */
1356 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001357
Christopher Faulet61cc8522020-04-20 14:54:42 +02001358 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1359 * messageID: 0x02 0x01 0x01: INTEGER 1
1360 * protocolOp: 0x61: bindResponse
1361 */
1362 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1363 status = HCHK_STATUS_L7RSP;
1364 desc = ist("Not LDAPv3 protocol");
1365 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001366 }
Simon Horman98637e52014-06-20 12:30:16 +09001367
Christopher Faulet61cc8522020-04-20 14:54:42 +02001368 /* size of bindResponse */
1369 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001370
Christopher Faulet61cc8522020-04-20 14:54:42 +02001371 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1372 * ldapResult: 0x0a 0x01: ENUMERATION
1373 */
1374 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1375 status = HCHK_STATUS_L7RSP;
1376 desc = ist("Not LDAPv3 protocol");
1377 goto error;
1378 }
Simon Horman98637e52014-06-20 12:30:16 +09001379
Christopher Faulet61cc8522020-04-20 14:54:42 +02001380 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1381 * resultCode
1382 */
1383 check->code = *(b_head(&check->bi) + msglen + 9);
1384 if (check->code) {
1385 status = HCHK_STATUS_L7STS;
1386 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1387 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001388 }
1389
Christopher Faulet1941bab2020-05-05 07:55:50 +02001390 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1391 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001392
Christopher Faulet61cc8522020-04-20 14:54:42 +02001393 out:
1394 free_trash_chunk(msg);
1395 return ret;
1396
1397 error:
1398 ret = TCPCHK_EVAL_STOP;
1399 msg = alloc_trash_chunk();
1400 if (msg)
1401 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1402 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1403 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001404}
1405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1407 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1408 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001409 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001411{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1413 enum healthcheck_status status;
1414 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001415 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001417
Willy Tarreaubaaee002006-06-26 02:48:02 +02001418
Christopher Faulet61cc8522020-04-20 14:54:42 +02001419 memcpy(&framesz, b_head(&check->bi), 4);
1420 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001421
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 if (!last_read && b_data(&check->bi) < (4+framesz))
1423 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001424
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425 memset(b_orig(&trash), 0, b_size(&trash));
1426 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1427 status = HCHK_STATUS_L7RSP;
1428 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1429 goto error;
1430 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001431
Christopher Faulet1941bab2020-05-05 07:55:50 +02001432 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1433 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001434
Christopher Faulet61cc8522020-04-20 14:54:42 +02001435 out:
1436 free_trash_chunk(msg);
1437 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001438
Christopher Faulet61cc8522020-04-20 14:54:42 +02001439 error:
1440 ret = TCPCHK_EVAL_STOP;
1441 msg = alloc_trash_chunk();
1442 if (msg)
1443 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1444 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1445 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001446
Christopher Faulet61cc8522020-04-20 14:54:42 +02001447 wait_more_data:
1448 ret = TCPCHK_EVAL_WAIT;
1449 goto out;
1450}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001451
Christopher Faulet61cc8522020-04-20 14:54:42 +02001452/* Custom tcp-check expect function to parse and validate the agent-check
1453 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1454 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1455 */
1456static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1457{
1458 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1459 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1460 const char *hs = NULL; /* health status */
1461 const char *as = NULL; /* admin status */
1462 const char *ps = NULL; /* performance status */
1463 const char *cs = NULL; /* maxconn */
1464 const char *err = NULL; /* first error to report */
1465 const char *wrn = NULL; /* first warning to report */
1466 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001467
Christopher Faulet61cc8522020-04-20 14:54:42 +02001468 /* We're getting an agent check response. The agent could
1469 * have been disabled in the mean time with a long check
1470 * still pending. It is important that we ignore the whole
1471 * response.
1472 */
1473 if (!(check->state & CHK_ST_ENABLED))
1474 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001475
Christopher Faulet61cc8522020-04-20 14:54:42 +02001476 /* The agent supports strings made of a single line ended by the
1477 * first CR ('\r') or LF ('\n'). This line is composed of words
1478 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1479 * line may optionally contained a description of a state change
1480 * after a sharp ('#'), which is only considered if a health state
1481 * is announced.
1482 *
1483 * Words may be composed of :
1484 * - a numeric weight suffixed by the percent character ('%').
1485 * - a health status among "up", "down", "stopped", and "fail".
1486 * - an admin status among "ready", "drain", "maint".
1487 *
1488 * These words may appear in any order. If multiple words of the
1489 * same category appear, the last one wins.
1490 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001491
Christopher Faulet61cc8522020-04-20 14:54:42 +02001492 p = b_head(&check->bi);
1493 while (*p && *p != '\n' && *p != '\r')
1494 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001495
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496 if (!*p) {
1497 if (!last_read)
1498 goto wait_more_data;
1499
1500 /* at least inform the admin that the agent is mis-behaving */
1501 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1502 goto out;
1503 }
1504
1505 *p = 0;
1506 cmd = b_head(&check->bi);
1507
1508 while (*cmd) {
1509 /* look for next word */
1510 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1511 cmd++;
1512 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001513 }
1514
Christopher Faulet61cc8522020-04-20 14:54:42 +02001515 if (*cmd == '#') {
1516 /* this is the beginning of a health status description,
1517 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001518 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001519 cmd++;
1520 while (*cmd == '\t' || *cmd == ' ')
1521 cmd++;
1522 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001523 }
1524
Christopher Faulet61cc8522020-04-20 14:54:42 +02001525 /* find the end of the word so that we have a null-terminated
1526 * word between <cmd> and <p>.
1527 */
1528 p = cmd + 1;
1529 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1530 p++;
1531 if (*p)
1532 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001533
Christopher Faulet61cc8522020-04-20 14:54:42 +02001534 /* first, health statuses */
1535 if (strcasecmp(cmd, "up") == 0) {
1536 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1537 status = HCHK_STATUS_L7OKD;
1538 hs = cmd;
1539 }
1540 else if (strcasecmp(cmd, "down") == 0) {
1541 check->server->check.health = 0;
1542 status = HCHK_STATUS_L7STS;
1543 hs = cmd;
1544 }
1545 else if (strcasecmp(cmd, "stopped") == 0) {
1546 check->server->check.health = 0;
1547 status = HCHK_STATUS_L7STS;
1548 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001549 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001550 else if (strcasecmp(cmd, "fail") == 0) {
1551 check->server->check.health = 0;
1552 status = HCHK_STATUS_L7STS;
1553 hs = cmd;
1554 }
1555 /* admin statuses */
1556 else if (strcasecmp(cmd, "ready") == 0) {
1557 as = cmd;
1558 }
1559 else if (strcasecmp(cmd, "drain") == 0) {
1560 as = cmd;
1561 }
1562 else if (strcasecmp(cmd, "maint") == 0) {
1563 as = cmd;
1564 }
1565 /* try to parse a weight here and keep the last one */
1566 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1567 ps = cmd;
1568 }
1569 /* try to parse a maxconn here */
1570 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1571 cs = cmd;
1572 }
1573 else {
1574 /* keep a copy of the first error */
1575 if (!err)
1576 err = cmd;
1577 }
1578 /* skip to next word */
1579 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001580 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001581 /* here, cmd points either to \0 or to the beginning of a
1582 * description. Skip possible leading spaces.
1583 */
1584 while (*cmd == ' ' || *cmd == '\n')
1585 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001586
Christopher Faulet61cc8522020-04-20 14:54:42 +02001587 /* First, update the admin status so that we avoid sending other
1588 * possibly useless warnings and can also update the health if
1589 * present after going back up.
1590 */
1591 if (as) {
1592 if (strcasecmp(as, "drain") == 0)
1593 srv_adm_set_drain(check->server);
1594 else if (strcasecmp(as, "maint") == 0)
1595 srv_adm_set_maint(check->server);
1596 else
1597 srv_adm_set_ready(check->server);
1598 }
Simon Horman98637e52014-06-20 12:30:16 +09001599
Christopher Faulet61cc8522020-04-20 14:54:42 +02001600 /* now change weights */
1601 if (ps) {
1602 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001603
Christopher Faulet61cc8522020-04-20 14:54:42 +02001604 msg = server_parse_weight_change_request(check->server, ps);
1605 if (!wrn || !*wrn)
1606 wrn = msg;
1607 }
Simon Horman98637e52014-06-20 12:30:16 +09001608
Christopher Faulet61cc8522020-04-20 14:54:42 +02001609 if (cs) {
1610 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001611
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001613
Christopher Faulet61cc8522020-04-20 14:54:42 +02001614 msg = server_parse_maxconn_change_request(check->server, cs);
1615 if (!wrn || !*wrn)
1616 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001617 }
1618
Christopher Faulet61cc8522020-04-20 14:54:42 +02001619 /* and finally health status */
1620 if (hs) {
1621 /* We'll report some of the warnings and errors we have
1622 * here. Down reports are critical, we leave them untouched.
1623 * Lack of report, or report of 'UP' leaves the room for
1624 * ERR first, then WARN.
1625 */
1626 const char *msg = cmd;
1627 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001628
Christopher Faulet61cc8522020-04-20 14:54:42 +02001629 if (!*msg || status == HCHK_STATUS_L7OKD) {
1630 if (err && *err)
1631 msg = err;
1632 else if (wrn && *wrn)
1633 msg = wrn;
1634 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001635
Christopher Faulet61cc8522020-04-20 14:54:42 +02001636 t = get_trash_chunk();
1637 chunk_printf(t, "via agent : %s%s%s%s",
1638 hs, *msg ? " (" : "",
1639 msg, *msg ? ")" : "");
1640 set_server_check_status(check, status, t->area);
1641 }
1642 else if (err && *err) {
1643 /* No status change but we'd like to report something odd.
1644 * Just report the current state and copy the message.
1645 */
1646 chunk_printf(&trash, "agent reports an error : %s", err);
1647 set_server_check_status(check, status/*check->status*/, trash.area);
1648 }
1649 else if (wrn && *wrn) {
1650 /* No status change but we'd like to report something odd.
1651 * Just report the current state and copy the message.
1652 */
1653 chunk_printf(&trash, "agent warns : %s", wrn);
1654 set_server_check_status(check, status/*check->status*/, trash.area);
1655 }
1656 else
1657 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001658
Christopher Faulet61cc8522020-04-20 14:54:42 +02001659 out:
1660 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001661
Christopher Faulet61cc8522020-04-20 14:54:42 +02001662 wait_more_data:
1663 ret = TCPCHK_EVAL_WAIT;
1664 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001665}
1666
Christopher Faulet61cc8522020-04-20 14:54:42 +02001667/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1668 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1669 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001670 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001671static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001672{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001673 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1674 struct tcpcheck_connect *connect = &rule->connect;
1675 struct proxy *proxy = check->proxy;
1676 struct server *s = check->server;
1677 struct task *t = check->task;
1678 struct conn_stream *cs;
1679 struct connection *conn = NULL;
1680 struct protocol *proto;
1681 struct xprt_ops *xprt;
1682 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001683
Christopher Faulet61cc8522020-04-20 14:54:42 +02001684 /* For a connect action we'll create a new connection. We may also have
1685 * to kill a previous one. But we don't want to leave *without* a
1686 * connection if we came here from the connection layer, hence with a
1687 * connection. Thus we'll proceed in the following order :
1688 * 1: close but not release previous connection (handled by the caller)
1689 * 2: try to get a new connection
1690 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001691 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001692
Christopher Faulet61cc8522020-04-20 14:54:42 +02001693 /* 2- prepare new connection */
1694 cs = cs_new(NULL);
1695 if (!cs) {
1696 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1697 tcpcheck_get_step_id(check, rule));
1698 if (rule->comment)
1699 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1700 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1701 ret = TCPCHK_EVAL_STOP;
1702 goto out;
1703 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001704
Christopher Faulet61cc8522020-04-20 14:54:42 +02001705 /* 3- release and replace the old one on success */
1706 if (check->cs) {
1707 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001708 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1709 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001710
1711 /* We may have been scheduled to run, and the I/O handler
1712 * expects to have a cs, so remove the tasklet
1713 */
1714 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1715 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001716 }
1717
Christopher Faulet61cc8522020-04-20 14:54:42 +02001718 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001719
Christopher Faulet61cc8522020-04-20 14:54:42 +02001720 check->cs = cs;
1721 conn = cs->conn;
1722 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001723
Christopher Faulet61cc8522020-04-20 14:54:42 +02001724 /* Maybe there were an older connection we were waiting on */
1725 check->wait_list.events = 0;
1726 conn->target = s ? &s->obj_type : &proxy->obj_type;
1727
1728 /* no client address */
1729 if (!sockaddr_alloc(&conn->dst)) {
1730 status = SF_ERR_RESOURCE;
1731 goto fail_check;
1732 }
1733
1734 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001735 * addr if specified on the server. otherwise, use the server addr (it
1736 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001737 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001738 *conn->dst = (is_addr(&connect->addr)
1739 ? connect->addr
1740 : (is_addr(&check->addr) ? check->addr : s->addr));
1741 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001742
Christopher Faulet61cc8522020-04-20 14:54:42 +02001743 port = 0;
1744 if (!port && connect->port)
1745 port = connect->port;
1746 if (!port && connect->port_expr) {
1747 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001748
Christopher Faulet61cc8522020-04-20 14:54:42 +02001749 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1750 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1751 connect->port_expr, SMP_T_SINT);
1752 if (smp)
1753 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001754 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001755 if (!port && is_inet_addr(&connect->addr))
1756 port = get_host_port(&connect->addr);
1757 if (!port && check->port)
1758 port = check->port;
1759 if (!port && is_inet_addr(&check->addr))
1760 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001761 if (!port) {
1762 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001763 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001764 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001765 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001766
Christopher Faulet61cc8522020-04-20 14:54:42 +02001767 xprt = ((connect->options & TCPCHK_OPT_SSL)
1768 ? xprt_get(XPRT_SSL)
1769 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001770
Christopher Faulet61cc8522020-04-20 14:54:42 +02001771 conn_prepare(conn, proto, xprt);
1772 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001773
Christopher Faulet61cc8522020-04-20 14:54:42 +02001774 status = SF_ERR_INTERNAL;
1775 if (proto && proto->connect) {
1776 struct tcpcheck_rule *next;
1777 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001778
Christopher Faulet61cc8522020-04-20 14:54:42 +02001779 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1780 flags |= CONNECT_HAS_DATA;
Christopher Faulet206368d2020-04-03 14:51:06 +02001781
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
1783 if (!next || next->action != TCPCHK_ACT_EXPECT)
1784 flags |= CONNECT_DELACK_ALWAYS;
1785 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001786 }
1787
Christopher Faulet61cc8522020-04-20 14:54:42 +02001788 if (status != SF_ERR_NONE)
1789 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001790
Christopher Faulet61cc8522020-04-20 14:54:42 +02001791 conn->flags |= CO_FL_PRIVATE;
1792 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001793
Christopher Faulet61cc8522020-04-20 14:54:42 +02001794 /* The mux may be initialized now if there isn't server attached to the
1795 * check (email alerts) or if there is a mux proto specified or if there
1796 * is no alpn.
1797 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001798 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1799 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001800 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001801
Christopher Faulet61cc8522020-04-20 14:54:42 +02001802 if (connect->mux_proto)
1803 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001804 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001805 mux_ops = check->mux_proto->mux;
1806 else {
1807 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1808 ? PROTO_MODE_HTTP
1809 : PROTO_MODE_TCP);
1810
1811 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001812 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001813 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1814 status = SF_ERR_INTERNAL;
1815 goto fail_check;
1816 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001817 }
1818
Christopher Faulet61cc8522020-04-20 14:54:42 +02001819#ifdef USE_OPENSSL
1820 if (connect->sni)
1821 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001822 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001823 ssl_sock_set_servername(conn, s->check.sni);
1824
1825 if (connect->alpn)
1826 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001827 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001828 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1829#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001830 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001831 conn->send_proxy_ofs = 1;
1832 conn->flags |= CO_FL_SOCKS4;
1833 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001834 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001835 conn->send_proxy_ofs = 1;
1836 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001837 }
1838
Christopher Faulet61cc8522020-04-20 14:54:42 +02001839 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1840 conn->send_proxy_ofs = 1;
1841 conn->flags |= CO_FL_SEND_PROXY;
1842 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001843 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001844 conn->send_proxy_ofs = 1;
1845 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001846 }
1847
Christopher Faulet61cc8522020-04-20 14:54:42 +02001848 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1849 /* Some servers don't like reset on close */
1850 fdtab[cs->conn->handle.fd].linger_risk = 0;
1851 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001852
Christopher Faulet61cc8522020-04-20 14:54:42 +02001853 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1854 if (xprt_add_hs(conn) < 0)
1855 status = SF_ERR_RESOURCE;
1856 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001857
Christopher Faulet61cc8522020-04-20 14:54:42 +02001858 fail_check:
1859 /* It can return one of :
1860 * - SF_ERR_NONE if everything's OK
1861 * - SF_ERR_SRVTO if there are no more servers
1862 * - SF_ERR_SRVCL if the connection was refused by the server
1863 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1864 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1865 * - SF_ERR_INTERNAL for any other purely internal errors
1866 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1867 * Note that we try to prevent the network stack from sending the ACK during the
1868 * connect() when a pure TCP check is used (without PROXY protocol).
1869 */
1870 switch (status) {
1871 case SF_ERR_NONE:
1872 /* we allow up to min(inter, timeout.connect) for a connection
1873 * to establish but only when timeout.check is set as it may be
1874 * to short for a full check otherwise
1875 */
1876 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001877
Christopher Faulet61cc8522020-04-20 14:54:42 +02001878 if (proxy->timeout.check && proxy->timeout.connect) {
1879 int t_con = tick_add(now_ms, proxy->timeout.connect);
1880 t->expire = tick_first(t->expire, t_con);
1881 }
1882 break;
1883 case SF_ERR_SRVTO: /* ETIMEDOUT */
1884 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1885 case SF_ERR_PRXCOND:
1886 case SF_ERR_RESOURCE:
1887 case SF_ERR_INTERNAL:
1888 chk_report_conn_err(check, errno, 0);
1889 ret = TCPCHK_EVAL_STOP;
1890 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001891 }
1892
Christopher Faulet61cc8522020-04-20 14:54:42 +02001893 /* don't do anything until the connection is established */
1894 if (conn->flags & CO_FL_WAIT_XPRT) {
1895 ret = TCPCHK_EVAL_WAIT;
1896 goto out;
1897 }
1898
1899 out:
1900 if (conn && check->result == CHK_RES_FAILED)
1901 conn->flags |= CO_FL_ERROR;
1902 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001903}
1904
Christopher Faulet61cc8522020-04-20 14:54:42 +02001905/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1906 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1907 * TCPCHK_EVAL_STOP if an error occurred.
1908 */
1909static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001910{
1911 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001912 struct tcpcheck_send *send = &rule->send;
1913 struct conn_stream *cs = check->cs;
1914 struct connection *conn = cs_conn(cs);
1915 struct buffer *tmp = NULL;
1916 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001917
Christopher Faulet61cc8522020-04-20 14:54:42 +02001918 /* reset the read & write buffer */
1919 b_reset(&check->bi);
1920 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001921
Christopher Faulet61cc8522020-04-20 14:54:42 +02001922 switch (send->type) {
1923 case TCPCHK_SEND_STRING:
1924 case TCPCHK_SEND_BINARY:
1925 if (istlen(send->data) >= b_size(&check->bo)) {
1926 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1927 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1928 tcpcheck_get_step_id(check, rule));
1929 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1930 ret = TCPCHK_EVAL_STOP;
1931 goto out;
1932 }
1933 b_putist(&check->bo, send->data);
1934 break;
1935 case TCPCHK_SEND_STRING_LF:
1936 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1937 if (!b_data(&check->bo))
1938 goto out;
1939 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001940 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001941 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001942
Christopher Faulet61cc8522020-04-20 14:54:42 +02001943 tmp = alloc_trash_chunk();
1944 if (!tmp)
1945 goto error_lf;
1946 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1947 if (!b_data(tmp))
1948 goto out;
1949 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001950 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001951 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001952 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001953 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001954 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001955 case TCPCHK_SEND_HTTP: {
1956 struct htx_sl *sl;
1957 struct ist meth, uri, vsn, clen, body;
1958 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001959
Christopher Faulet61cc8522020-04-20 14:54:42 +02001960 tmp = alloc_trash_chunk();
1961 if (!tmp)
1962 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001963
Christopher Faulet61cc8522020-04-20 14:54:42 +02001964 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1965 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1966 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001967 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1968 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1969 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1970 }
1971 else
1972 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001973 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001974
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001975 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1976 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001977 slflags |= HTX_SL_F_VER_11;
1978 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1979 if (!isttest(send->http.body))
1980 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001981
Christopher Faulet61cc8522020-04-20 14:54:42 +02001982 htx = htx_from_buf(&check->bo);
1983 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1984 if (!sl)
1985 goto error_htx;
1986 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001987 if (!http_update_host(htx, sl, uri))
1988 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001989
Christopher Faulet61cc8522020-04-20 14:54:42 +02001990 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1991 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001992 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001993
Christopher Faulet61cc8522020-04-20 14:54:42 +02001994 list_for_each_entry(hdr, &send->http.hdrs, list) {
1995 chunk_reset(tmp);
1996 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
1997 if (!b_data(tmp))
1998 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02001999 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2000 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002001 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002002 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2003 if (!http_update_authority(htx, sl, hdr_value))
2004 goto error_htx;
2005 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002006 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002007
Christopher Faulet61cc8522020-04-20 14:54:42 +02002008 }
2009 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2010 chunk_reset(tmp);
2011 httpchk_build_status_header(check->server, tmp);
2012 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2013 goto error_htx;
2014 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002015
2016
2017 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2018 chunk_reset(tmp);
2019 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2020 body = ist2(b_orig(tmp), b_data(tmp));
2021 }
2022 else
2023 body = send->http.body;
2024 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2025
2026 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2027 !htx_add_header(htx, ist("Content-length"), clen))
2028 goto error_htx;
2029
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002030
Christopher Faulet61cc8522020-04-20 14:54:42 +02002031 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002032 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002033 !htx_add_endof(htx, HTX_BLK_EOM))
2034 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002035
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 htx_to_buf(htx, &check->bo);
2037 break;
2038 }
2039 case TCPCHK_SEND_UNDEF:
2040 /* Should never happen. */
2041 ret = TCPCHK_EVAL_STOP;
2042 goto out;
2043 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002044
Christopher Faulet6d471212020-04-22 11:09:25 +02002045
2046 if (conn->mux->snd_buf(cs, &check->bo,
2047 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002048 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002049 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002050 goto out;
2051 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002052 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002053 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002054 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2055 ret = TCPCHK_EVAL_WAIT;
2056 goto out;
2057 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002058
Christopher Faulet61cc8522020-04-20 14:54:42 +02002059 out:
2060 free_trash_chunk(tmp);
2061 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002062
Christopher Faulet61cc8522020-04-20 14:54:42 +02002063 error_htx:
2064 if (htx) {
2065 htx_reset(htx);
2066 htx_to_buf(htx, &check->bo);
2067 }
2068 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2069 tcpcheck_get_step_id(check, rule));
2070 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2071 ret = TCPCHK_EVAL_STOP;
2072 goto out;
2073
2074 error_lf:
2075 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2076 tcpcheck_get_step_id(check, rule));
2077 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2078 ret = TCPCHK_EVAL_STOP;
2079 goto out;
2080
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002081}
2082
Christopher Faulet61cc8522020-04-20 14:54:42 +02002083/* Try to reveice data before evaluting a tcp-check expect rule. Returns
2084 * TCPCHK_EVAL_WAIT if it is already subcribed on receive events or if nothing
2085 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2086 * TCPCHK_EVAL_STOP if an error occurred.
2087 */
2088static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002089{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002090 struct conn_stream *cs = check->cs;
2091 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002092 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002093 size_t max, read, cur_read = 0;
2094 int is_empty;
2095 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002096
Christopher Faulet61cc8522020-04-20 14:54:42 +02002097 if (check->wait_list.events & SUB_RETRY_RECV)
2098 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002099
Christopher Faulet61cc8522020-04-20 14:54:42 +02002100 if (cs->flags & CS_FL_EOS)
2101 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002102
Christopher Faulet61cc8522020-04-20 14:54:42 +02002103 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002104
Christopher Faulet61cc8522020-04-20 14:54:42 +02002105 /* prepare to detect if the mux needs more room */
2106 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002107
Christopher Faulet61cc8522020-04-20 14:54:42 +02002108 while ((cs->flags & CS_FL_RCV_MORE) ||
2109 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2110 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2111 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2112 cur_read += read;
2113 if (!read ||
2114 (cs->flags & CS_FL_WANT_ROOM) ||
2115 (--read_poll <= 0) ||
2116 (read < max && read >= global.tune.recv_enough))
2117 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002118 }
2119
Christopher Faulet61cc8522020-04-20 14:54:42 +02002120 end_recv:
2121 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2122 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2123 /* Report network errors only if we got no other data. Otherwise
2124 * we'll let the upper layers decide whether the response is OK
2125 * or not. It is very common that an RST sent by the server is
2126 * reported as an error just after the last data chunk.
2127 */
2128 goto stop;
2129 }
2130 if (!cur_read) {
2131 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2132 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2133 goto wait_more_data;
2134 }
2135 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002136 int status;
2137
Christopher Faulet61cc8522020-04-20 14:54:42 +02002138 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2139 tcpcheck_get_step_id(check, rule));
2140 if (rule->comment)
2141 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002142
2143 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2144 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002145 goto stop;
2146 }
2147 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002148
2149 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002150 return ret;
2151
Christopher Faulet61cc8522020-04-20 14:54:42 +02002152 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002153 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002154 goto out;
2155
2156 wait_more_data:
2157 ret = TCPCHK_EVAL_WAIT;
2158 goto out;
2159}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002160
Christopher Faulet61cc8522020-04-20 14:54:42 +02002161/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2162 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2163 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2164 * error occurred.
2165 */
2166static 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 +02002167{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002168 struct htx *htx = htxbuf(&check->bi);
2169 struct htx_sl *sl;
2170 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002171 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002172 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002173 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002174 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002175 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002176 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002177
Christopher Faulet61cc8522020-04-20 14:54:42 +02002178 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002179
Christopher Faulet61cc8522020-04-20 14:54:42 +02002180 if (htx->flags & HTX_FL_PARSING_ERROR) {
2181 status = HCHK_STATUS_L7RSP;
2182 goto error;
2183 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002184
Christopher Faulet61cc8522020-04-20 14:54:42 +02002185 if (htx_is_empty(htx)) {
2186 if (last_read) {
2187 status = HCHK_STATUS_L7RSP;
2188 goto error;
2189 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002190 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002191 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002192
Christopher Faulet61cc8522020-04-20 14:54:42 +02002193 sl = http_get_stline(htx);
2194 check->code = sl->info.res.status;
2195
2196 if (check->server &&
2197 (check->server->proxy->options & PR_O_DISABLE404) &&
2198 (check->server->next_state != SRV_ST_STOPPED) &&
2199 (check->code == 404)) {
2200 /* 404 may be accepted as "stopping" only if the server was up */
2201 goto out;
2202 }
2203
2204 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2205 /* Make GCC happy ; initialize match to a failure state. */
2206 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002207 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002208
2209 switch (expect->type) {
2210 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002211 match = 0;
2212 for (i = 0; i < expect->codes.num; i++) {
2213 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2214 sl->info.res.status <= expect->codes.codes[i][1]) {
2215 match = 1;
2216 break;
2217 }
2218 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002219
2220 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002221 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002222 if (LIST_ISEMPTY(&expect->onerror_fmt))
2223 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002224 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002225 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002226 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2227
2228 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002229 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002230 if (LIST_ISEMPTY(&expect->onerror_fmt))
2231 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002232 break;
2233
Christopher Faulet39708192020-05-05 10:47:36 +02002234 case TCPCHK_EXPECT_HTTP_HEADER: {
2235 struct http_hdr_ctx ctx;
2236 struct ist npat, vpat, value;
2237 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2238
2239 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2240 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002241 if (!nbuf) {
2242 status = HCHK_STATUS_L7RSP;
2243 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002244 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002245 }
Christopher Faulet39708192020-05-05 10:47:36 +02002246 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 +02002247 if (!b_data(nbuf)) {
2248 status = HCHK_STATUS_L7RSP;
2249 desc = ist("log-format string evaluated to an empty string");
2250 goto error;
2251 }
Christopher Faulet39708192020-05-05 10:47:36 +02002252 npat = ist2(b_orig(nbuf), b_data(nbuf));
2253 }
2254 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2255 npat = expect->hdr.name;
2256
2257 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2258 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002259 if (!vbuf) {
2260 status = HCHK_STATUS_L7RSP;
2261 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002262 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002263 }
Christopher Faulet39708192020-05-05 10:47:36 +02002264 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 +02002265 if (!b_data(vbuf)) {
2266 status = HCHK_STATUS_L7RSP;
2267 desc = ist("log-format string evaluated to an empty string");
2268 goto error;
2269 }
Christopher Faulet39708192020-05-05 10:47:36 +02002270 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2271 }
2272 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2273 vpat = expect->hdr.value;
2274
2275 match = 0;
2276 ctx.blk = NULL;
2277 while (1) {
2278 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2279 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2280 if (!http_find_str_header(htx, npat, &ctx, full))
2281 goto end_of_match;
2282 break;
2283 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2284 if (!http_find_pfx_header(htx, npat, &ctx, full))
2285 goto end_of_match;
2286 break;
2287 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2288 if (!http_find_sfx_header(htx, npat, &ctx, full))
2289 goto end_of_match;
2290 break;
2291 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2292 if (!http_find_sub_header(htx, npat, &ctx, full))
2293 goto end_of_match;
2294 break;
2295 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2296 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2297 goto end_of_match;
2298 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002299 default:
2300 /* should never happen */
2301 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002302 }
2303
Christopher Faulet083eff32020-05-07 15:41:39 +02002304 /* A header has matched the name pattern, let's test its
2305 * value now (always defined from there). If there is no
2306 * value pattern, it is a good match.
2307 */
2308
Christopher Faulet39708192020-05-05 10:47:36 +02002309 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2310 match = 1;
2311 goto end_of_match;
2312 }
2313
2314 value = ctx.value;
2315 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2316 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2317 if (isteq(value, vpat)) {
2318 match = 1;
2319 goto end_of_match;
2320 }
2321 break;
2322 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2323 if (istlen(value) < istlen(vpat))
2324 break;
2325 value = ist2(istptr(value), istlen(vpat));
2326 if (isteq(value, vpat)) {
2327 match = 1;
2328 goto end_of_match;
2329 }
2330 break;
2331 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2332 if (istlen(value) < istlen(vpat))
2333 break;
2334 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2335 if (isteq(value, vpat)) {
2336 match = 1;
2337 goto end_of_match;
2338 }
2339 break;
2340 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2341 if (isttest(istist(value, vpat))) {
2342 match = 1;
2343 goto end_of_match;
2344 }
2345 break;
2346 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2347 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2348 match = 1;
2349 goto end_of_match;
2350 }
2351 break;
2352 }
2353 }
2354
2355 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002356 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002357 if (LIST_ISEMPTY(&expect->onerror_fmt))
2358 desc = htx_sl_res_reason(sl);
2359 break;
2360 }
2361
Christopher Faulet61cc8522020-04-20 14:54:42 +02002362 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002363 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002364 case TCPCHK_EXPECT_HTTP_BODY_LF:
2365 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002366 chunk_reset(&trash);
2367 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2368 enum htx_blk_type type = htx_get_blk_type(blk);
2369
2370 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2371 break;
2372 if (type == HTX_BLK_DATA) {
2373 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2374 break;
2375 }
2376 }
2377
2378 if (!b_data(&trash)) {
2379 if (!last_read)
2380 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002381 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002382 if (LIST_ISEMPTY(&expect->onerror_fmt))
2383 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002384 goto error;
2385 }
2386
Christopher Fauletaaab0832020-05-05 15:54:22 +02002387 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2388 tmp = alloc_trash_chunk();
2389 if (!tmp) {
2390 status = HCHK_STATUS_L7RSP;
2391 desc = ist("Failed to allocate buffer to eval log-format string");
2392 goto error;
2393 }
2394 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2395 if (!b_data(tmp)) {
2396 status = HCHK_STATUS_L7RSP;
2397 desc = ist("log-format string evaluated to an empty string");
2398 goto error;
2399 }
2400 }
2401
Christopher Faulet61cc8522020-04-20 14:54:42 +02002402 if (!last_read &&
2403 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002404 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002405 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2406 ret = TCPCHK_EVAL_WAIT;
2407 goto out;
2408 }
2409
2410 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002411 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002412 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2413 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002414 else
2415 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2416
2417 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002418 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002419 if (LIST_ISEMPTY(&expect->onerror_fmt))
2420 desc = (inverse
2421 ? ist("HTTP check matched unwanted content")
2422 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002423 break;
2424
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002425
Christopher Faulet61cc8522020-04-20 14:54:42 +02002426 default:
2427 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002428 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002429 goto error;
2430 }
2431
Christopher Faulet61cc8522020-04-20 14:54:42 +02002432 /* Wait for more data on mismatch only if no minimum is defined (-1),
2433 * otherwise the absence of match is already conclusive.
2434 */
2435 if (!match && !last_read && (expect->min_recv == -1)) {
2436 ret = TCPCHK_EVAL_WAIT;
2437 goto out;
2438 }
2439
2440 if (!(match ^ inverse))
2441 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002442
2443 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002444 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002445 free_trash_chunk(nbuf);
2446 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002447 free_trash_chunk(msg);
2448 return ret;
2449
2450 error:
2451 ret = TCPCHK_EVAL_STOP;
2452 msg = alloc_trash_chunk();
2453 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002454 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002455 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2456 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002457
2458 wait_more_data:
2459 ret = TCPCHK_EVAL_WAIT;
2460 goto out;
2461}
2462
Christopher Faulet61cc8522020-04-20 14:54:42 +02002463/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2464 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2465 * if an error occurred.
2466 */
2467static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2468{
2469 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2470 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002471 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002472 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002473 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002474 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002475
Christopher Faulet61cc8522020-04-20 14:54:42 +02002476 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002477
Christopher Faulet61cc8522020-04-20 14:54:42 +02002478 /* The current expect might need more data than the previous one, check again
2479 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002480 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 if (!last_read) {
2482 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2483 (b_data(&check->bi) < istlen(expect->data))) {
2484 ret = TCPCHK_EVAL_WAIT;
2485 goto out;
2486 }
2487 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2488 ret = TCPCHK_EVAL_WAIT;
2489 goto out;
2490 }
2491 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002492
Christopher Faulet61cc8522020-04-20 14:54:42 +02002493 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2494 /* Make GCC happy ; initialize match to a failure state. */
2495 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002496 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002497
Christopher Faulet61cc8522020-04-20 14:54:42 +02002498 switch (expect->type) {
2499 case TCPCHK_EXPECT_STRING:
2500 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002501 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 +02002502 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002503 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002504 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 +02002505 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002506
Christopher Faulet67a23452020-05-05 18:10:01 +02002507 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002508 chunk_reset(&trash);
2509 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002510 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002511 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002512
2513 case TCPCHK_EXPECT_STRING_LF:
2514 case TCPCHK_EXPECT_BINARY_LF:
2515 match = 0;
2516 tmp = alloc_trash_chunk();
2517 if (!tmp) {
2518 status = HCHK_STATUS_L7RSP;
2519 desc = ist("Failed to allocate buffer to eval format string");
2520 goto error;
2521 }
2522 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2523 if (!b_data(tmp)) {
2524 status = HCHK_STATUS_L7RSP;
2525 desc = ist("log-format string evaluated to an empty string");
2526 goto error;
2527 }
2528 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2529 int len = tmp->data;
2530 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2531 status = HCHK_STATUS_L7RSP;
2532 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2533 goto error;
2534 }
2535 tmp->data = len;
2536 }
2537 if (b_data(&check->bi) < tmp->data) {
2538 if (!last_read) {
2539 ret = TCPCHK_EVAL_WAIT;
2540 goto out;
2541 }
2542 break;
2543 }
2544 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2545 break;
2546
Christopher Faulet61cc8522020-04-20 14:54:42 +02002547 case TCPCHK_EXPECT_CUSTOM:
2548 if (expect->custom)
2549 ret = expect->custom(check, rule, last_read);
2550 goto out;
2551 default:
2552 /* Should never happen. */
2553 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002554 goto out;
2555 }
2556
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002557
Christopher Faulet61cc8522020-04-20 14:54:42 +02002558 /* Wait for more data on mismatch only if no minimum is defined (-1),
2559 * otherwise the absence of match is already conclusive.
2560 */
2561 if (!match && !last_read && (expect->min_recv == -1)) {
2562 ret = TCPCHK_EVAL_WAIT;
2563 goto out;
2564 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002565
Christopher Faulet61cc8522020-04-20 14:54:42 +02002566 /* Result as expected, next rule. */
2567 if (match ^ inverse)
2568 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002569
Christopher Fauletaaab0832020-05-05 15:54:22 +02002570 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002571 /* From this point on, we matched something we did not want, this is an error state. */
2572 ret = TCPCHK_EVAL_STOP;
2573 msg = alloc_trash_chunk();
2574 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002575 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002576 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002577 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002578
Christopher Faulet61cc8522020-04-20 14:54:42 +02002579 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002580 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002581 return ret;
2582}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002583
Christopher Faulet61cc8522020-04-20 14:54:42 +02002584/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
2585 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It nevers
2586 * waits.
2587 */
2588static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2589{
2590 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2591 struct act_rule *act_rule;
2592 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002593
Christopher Faulet61cc8522020-04-20 14:54:42 +02002594 act_rule =rule->action_kw.rule;
2595 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2596 if (act_ret != ACT_RET_CONT) {
2597 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2598 tcpcheck_get_step_id(check, rule));
2599 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2600 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002601 }
2602
Christopher Faulet61cc8522020-04-20 14:54:42 +02002603 return ret;
2604}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002605
Christopher Faulet61cc8522020-04-20 14:54:42 +02002606/* Executes a tcp-check ruleset. Note that this is called both from the
2607 * connection's wake() callback and from the check scheduling task. It returns
2608 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2609 * presenting the risk of an fd replacement.
2610 *
2611 * Please do NOT place any return statement in this function and only leave
2612 * via the out_end_tcpcheck label after setting retcode.
2613 */
2614static int tcpcheck_main(struct check *check)
2615{
2616 struct tcpcheck_rule *rule;
2617 struct conn_stream *cs = check->cs;
2618 struct connection *conn = cs_conn(cs);
2619 int must_read = 1, last_read = 0;
2620 int ret, retcode = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002621
Christopher Faulet61cc8522020-04-20 14:54:42 +02002622 /* here, we know that the check is complete or that it failed */
2623 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002624 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002625
Christopher Faulet61cc8522020-04-20 14:54:42 +02002626 /* 1- check for connection error, if any */
2627 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2628 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002629
Christopher Faulet61cc8522020-04-20 14:54:42 +02002630 /* 2- check if we are waiting for the connection establishment. It only
2631 * happens during TCPCHK_ACT_CONNECT. */
2632 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
2633 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
2634 if (conn && (conn->flags & CO_FL_WAIT_XPRT)) {
2635 if (rule->action == TCPCHK_ACT_SEND)
2636 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2637 else if (rule->action == TCPCHK_ACT_EXPECT)
2638 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2639 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002640 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002641 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002642
2643 /* 3- check for pending outgoing data. It only happens during
2644 * TCPCHK_ACT_SEND. */
2645 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
2646 if (conn && b_data(&check->bo)) {
Christopher Faulet6d471212020-04-22 11:09:25 +02002647 ret = conn->mux->snd_buf(cs, &check->bo,
2648 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002649 if (ret <= 0) {
2650 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2651 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002652 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002653 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002654 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2655 goto out;
2656 }
2657 }
2658 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002659 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002660
Christopher Faulet61cc8522020-04-20 14:54:42 +02002661 /* 4- check if a rule must be resume. It happens if check->current_step
2662 * is defined. */
2663 else if (check->current_step)
2664 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002665
Christopher Faulet61cc8522020-04-20 14:54:42 +02002666 /* 5- It is the first evaluation. We must create a session and preset
2667 * tcp-check variables */
2668 else {
2669 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002670
Christopher Faulet61cc8522020-04-20 14:54:42 +02002671 /* First evaluation, create a session */
2672 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2673 if (!check->sess) {
2674 chunk_printf(&trash, "TCPCHK error allocating check session");
2675 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2676 goto out_end_tcpcheck;
2677 }
2678 vars_init(&check->vars, SCOPE_CHECK);
2679 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002680
Christopher Faulet61cc8522020-04-20 14:54:42 +02002681 /* Preset tcp-check variables */
2682 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2683 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002684
Christopher Faulet61cc8522020-04-20 14:54:42 +02002685 memset(&smp, 0, sizeof(smp));
2686 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2687 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002688 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002689 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002690 }
2691
Christopher Faulet61cc8522020-04-20 14:54:42 +02002692 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002693
Christopher Faulet61cc8522020-04-20 14:54:42 +02002694 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
2695 enum tcpcheck_eval_ret eval_ret;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002696
Christopher Faulet61cc8522020-04-20 14:54:42 +02002697 check->code = 0;
2698 switch (rule->action) {
2699 case TCPCHK_ACT_CONNECT:
2700 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002701
Christopher Faulet61cc8522020-04-20 14:54:42 +02002702 /* close but not release yet previous connection */
2703 if (check->cs) {
2704 cs_close(check->cs);
2705 retcode = -1; /* do not reuse the fd in the caller! */
2706 }
2707 eval_ret = tcpcheck_eval_connect(check, rule);
2708 must_read = 1; last_read = 0;
2709 break;
2710 case TCPCHK_ACT_SEND:
2711 check->current_step = rule;
2712 eval_ret = tcpcheck_eval_send(check, rule);
2713 must_read = 1;
2714 break;
2715 case TCPCHK_ACT_EXPECT:
2716 check->current_step = rule;
2717 if (must_read) {
2718 if (check->proxy->timeout.check)
2719 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002720
Christopher Faulet61cc8522020-04-20 14:54:42 +02002721 eval_ret = tcpcheck_eval_recv(check, rule);
2722 if (eval_ret == TCPCHK_EVAL_STOP)
2723 goto out_end_tcpcheck;
2724 else if (eval_ret == TCPCHK_EVAL_WAIT)
2725 goto out;
2726 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2727 must_read = 0;
2728 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002729
Christopher Faulet61cc8522020-04-20 14:54:42 +02002730 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2731 ? tcpcheck_eval_expect_http(check, rule, last_read)
2732 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002733
Christopher Faulet61cc8522020-04-20 14:54:42 +02002734 if (eval_ret == TCPCHK_EVAL_WAIT) {
2735 check->current_step = rule->expect.head;
2736 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2737 }
2738 break;
2739 case TCPCHK_ACT_ACTION_KW:
2740 /* Don't update the current step */
2741 eval_ret = tcpcheck_eval_action_kw(check, rule);
2742 break;
2743 default:
2744 /* Otherwise, just go to the next one and don't update
2745 * the current step
2746 */
2747 eval_ret = TCPCHK_EVAL_CONTINUE;
2748 break;
2749 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002750
Christopher Faulet61cc8522020-04-20 14:54:42 +02002751 switch (eval_ret) {
2752 case TCPCHK_EVAL_CONTINUE:
2753 break;
2754 case TCPCHK_EVAL_WAIT:
2755 goto out;
2756 case TCPCHK_EVAL_STOP:
2757 goto out_end_tcpcheck;
2758 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002759 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002760
Christopher Faulet61cc8522020-04-20 14:54:42 +02002761 /* All rules was evaluated */
2762 if (check->current_step) {
2763 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002764
Christopher Faulet61cc8522020-04-20 14:54:42 +02002765 if (rule->action == TCPCHK_ACT_EXPECT) {
2766 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002767 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002768
Christopher Faulet61cc8522020-04-20 14:54:42 +02002769 if (check->server &&
2770 (check->server->proxy->options & PR_O_DISABLE404) &&
2771 (check->server->next_state != SRV_ST_STOPPED) &&
2772 (check->code == 404)) {
2773 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2774 goto out_end_tcpcheck;
2775 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002776
Christopher Faulet61cc8522020-04-20 14:54:42 +02002777 msg = alloc_trash_chunk();
2778 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002779 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002780 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2781 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002782 free_trash_chunk(msg);
2783 }
2784 else if (rule->action == TCPCHK_ACT_CONNECT) {
2785 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002786 enum healthcheck_status status = HCHK_STATUS_L4OK;
2787#ifdef USE_OPENSSL
2788 if (conn && ssl_sock_is_ssl(conn))
2789 status = HCHK_STATUS_L6OK;
2790#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002791 set_server_check_status(check, status, msg);
2792 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002793 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002794 else
2795 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002796
Christopher Faulet61cc8522020-04-20 14:54:42 +02002797 out_end_tcpcheck:
2798 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2799 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002800
Christopher Faulet61cc8522020-04-20 14:54:42 +02002801 out:
2802 return retcode;
2803}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002804
Christopher Faulet14cd3162020-04-16 14:50:06 +02002805
Christopher Faulet61cc8522020-04-20 14:54:42 +02002806/**************************************************************************/
2807/************** Health-checks based on an external process ****************/
2808/**************************************************************************/
2809static struct list pid_list = LIST_HEAD_INIT(pid_list);
2810static struct pool_head *pool_head_pid_list;
2811__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002812
Christopher Faulet61cc8522020-04-20 14:54:42 +02002813struct extcheck_env {
2814 char *name; /* environment variable name */
2815 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2816};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002817
Christopher Faulet61cc8522020-04-20 14:54:42 +02002818/* environment variables memory requirement for different types of data */
2819#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2820 * such environment variables are not updatable. */
2821#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2822#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2823#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002824
Christopher Faulet61cc8522020-04-20 14:54:42 +02002825/* external checks environment variables */
2826enum {
2827 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002828
Christopher Faulet61cc8522020-04-20 14:54:42 +02002829 /* Proxy specific environment variables */
2830 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2831 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2832 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2833 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002834
Christopher Faulet61cc8522020-04-20 14:54:42 +02002835 /* Server specific environment variables */
2836 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2837 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2838 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2839 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2840 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2841 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002842
Christopher Faulet61cc8522020-04-20 14:54:42 +02002843 EXTCHK_SIZE
2844};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002845
Christopher Faulet61cc8522020-04-20 14:54:42 +02002846const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2847 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2848 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2849 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2850 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2851 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2852 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2853 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2854 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2855 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2856 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2857 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2858};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002859
Christopher Faulet61cc8522020-04-20 14:54:42 +02002860void block_sigchld(void)
2861{
2862 sigset_t set;
2863 sigemptyset(&set);
2864 sigaddset(&set, SIGCHLD);
2865 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2866}
Willy Tarreaube373152018-09-06 11:45:30 +02002867
Christopher Faulet61cc8522020-04-20 14:54:42 +02002868void unblock_sigchld(void)
2869{
2870 sigset_t set;
2871 sigemptyset(&set);
2872 sigaddset(&set, SIGCHLD);
2873 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002874}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002875
Christopher Faulet61cc8522020-04-20 14:54:42 +02002876static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002877{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002878 struct pid_list *elem;
2879 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002880
Christopher Faulet61cc8522020-04-20 14:54:42 +02002881 elem = pool_alloc(pool_head_pid_list);
2882 if (!elem)
2883 return NULL;
2884 elem->pid = pid;
2885 elem->t = t;
2886 elem->exited = 0;
2887 check->curpid = elem;
2888 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002889
Christopher Faulet61cc8522020-04-20 14:54:42 +02002890 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2891 LIST_ADD(&pid_list, &elem->list);
2892 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002893
Christopher Faulet61cc8522020-04-20 14:54:42 +02002894 return elem;
2895}
Christopher Faulete5870d82020-04-15 11:32:03 +02002896
Christopher Faulet61cc8522020-04-20 14:54:42 +02002897static void pid_list_del(struct pid_list *elem)
2898{
2899 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002900
Christopher Faulet61cc8522020-04-20 14:54:42 +02002901 if (!elem)
2902 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002903
Christopher Faulet61cc8522020-04-20 14:54:42 +02002904 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2905 LIST_DEL(&elem->list);
2906 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002907
Christopher Faulet61cc8522020-04-20 14:54:42 +02002908 if (!elem->exited)
2909 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002910
Christopher Faulet61cc8522020-04-20 14:54:42 +02002911 check = elem->t->context;
2912 check->curpid = NULL;
2913 pool_free(pool_head_pid_list, elem);
2914}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002915
Christopher Faulet61cc8522020-04-20 14:54:42 +02002916/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2917static void pid_list_expire(pid_t pid, int status)
2918{
2919 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002920
Christopher Faulet61cc8522020-04-20 14:54:42 +02002921 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2922 list_for_each_entry(elem, &pid_list, list) {
2923 if (elem->pid == pid) {
2924 elem->t->expire = now_ms;
2925 elem->status = status;
2926 elem->exited = 1;
2927 task_wakeup(elem->t, TASK_WOKEN_IO);
2928 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002929 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002930 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002931 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2932}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002933
Christopher Faulet61cc8522020-04-20 14:54:42 +02002934static void sigchld_handler(struct sig_handler *sh)
2935{
2936 pid_t pid;
2937 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002938
Christopher Faulet61cc8522020-04-20 14:54:42 +02002939 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2940 pid_list_expire(pid, status);
2941}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002942
Christopher Faulet61cc8522020-04-20 14:54:42 +02002943static int init_pid_list(void)
2944{
2945 if (pool_head_pid_list != NULL)
2946 /* Nothing to do */
2947 return 0;
2948
2949 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2950 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2951 strerror(errno));
2952 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002953 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002954
Christopher Faulet61cc8522020-04-20 14:54:42 +02002955 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2956 if (pool_head_pid_list == NULL) {
2957 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2958 strerror(errno));
2959 return 1;
2960 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002961
Christopher Faulet61cc8522020-04-20 14:54:42 +02002962 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002963}
2964
Christopher Faulet61cc8522020-04-20 14:54:42 +02002965/* helper macro to set an environment variable and jump to a specific label on failure. */
2966#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002967
Christopher Faulet61cc8522020-04-20 14:54:42 +02002968/*
2969 * helper function to allocate enough memory to store an environment variable.
2970 * It will also check that the environment variable is updatable, and silently
2971 * fail if not.
2972 */
2973static int extchk_setenv(struct check *check, int idx, const char *value)
2974{
2975 int len, ret;
2976 char *envname;
2977 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002978
Christopher Faulet61cc8522020-04-20 14:54:42 +02002979 if (idx < 0 || idx >= EXTCHK_SIZE) {
2980 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
2981 return 1;
2982 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002983
Christopher Faulet61cc8522020-04-20 14:54:42 +02002984 envname = extcheck_envs[idx].name;
2985 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002986
Christopher Faulet61cc8522020-04-20 14:54:42 +02002987 /* Check if the environment variable is already set, and silently reject
2988 * the update if this one is not updatable. */
2989 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
2990 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02002991
Christopher Faulet61cc8522020-04-20 14:54:42 +02002992 /* Instead of sending NOT_USED, sending an empty value is preferable */
2993 if (strcmp(value, "NOT_USED") == 0) {
2994 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02002995 }
2996
Christopher Faulet61cc8522020-04-20 14:54:42 +02002997 len = strlen(envname) + 1;
2998 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
2999 len += strlen(value);
3000 else
3001 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003002
Christopher Faulet61cc8522020-04-20 14:54:42 +02003003 if (!check->envp[idx])
3004 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003005
Christopher Faulet61cc8522020-04-20 14:54:42 +02003006 if (!check->envp[idx]) {
3007 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3008 return 1;
3009 }
3010 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3011 if (ret < 0) {
3012 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3013 return 1;
3014 }
3015 else if (ret > len) {
3016 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3017 return 1;
3018 }
3019 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003020}
3021
Christopher Faulet61cc8522020-04-20 14:54:42 +02003022static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003023{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003024 struct server *s = check->server;
3025 struct proxy *px = s->proxy;
3026 struct listener *listener = NULL, *l;
3027 int i;
3028 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3029 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003030
Christopher Faulet61cc8522020-04-20 14:54:42 +02003031 list_for_each_entry(l, &px->conf.listeners, by_fe)
3032 /* Use the first INET, INET6 or UNIX listener */
3033 if (l->addr.ss_family == AF_INET ||
3034 l->addr.ss_family == AF_INET6 ||
3035 l->addr.ss_family == AF_UNIX) {
3036 listener = l;
3037 break;
3038 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003039
Christopher Faulet61cc8522020-04-20 14:54:42 +02003040 check->curpid = NULL;
3041 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3042 if (!check->envp) {
3043 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3044 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003045 }
3046
Christopher Faulet61cc8522020-04-20 14:54:42 +02003047 check->argv = calloc(6, sizeof(char *));
3048 if (!check->argv) {
3049 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3050 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003051 }
3052
Christopher Faulet61cc8522020-04-20 14:54:42 +02003053 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003054
Christopher Faulet61cc8522020-04-20 14:54:42 +02003055 if (!listener) {
3056 check->argv[1] = strdup("NOT_USED");
3057 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003058 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003059 else if (listener->addr.ss_family == AF_INET ||
3060 listener->addr.ss_family == AF_INET6) {
3061 addr_to_str(&listener->addr, buf, sizeof(buf));
3062 check->argv[1] = strdup(buf);
3063 port_to_str(&listener->addr, buf, sizeof(buf));
3064 check->argv[2] = strdup(buf);
3065 }
3066 else if (listener->addr.ss_family == AF_UNIX) {
3067 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003068
Christopher Faulet61cc8522020-04-20 14:54:42 +02003069 un = (struct sockaddr_un *)&listener->addr;
3070 check->argv[1] = strdup(un->sun_path);
3071 check->argv[2] = strdup("NOT_USED");
3072 }
3073 else {
3074 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3075 goto err;
3076 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003077
Christopher Faulet61cc8522020-04-20 14:54:42 +02003078 if (!check->argv[1] || !check->argv[2]) {
3079 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3080 goto err;
3081 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003082
Christopher Faulet61cc8522020-04-20 14:54:42 +02003083 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3084 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3085 if (!check->argv[3] || !check->argv[4]) {
3086 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3087 goto err;
3088 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003089
Christopher Faulet61cc8522020-04-20 14:54:42 +02003090 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3091 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3092 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003093
Christopher Faulet61cc8522020-04-20 14:54:42 +02003094 for (i = 0; i < 5; i++) {
3095 if (!check->argv[i]) {
3096 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3097 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003098 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003099 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003100
Christopher Faulet61cc8522020-04-20 14:54:42 +02003101 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3102 /* Add proxy environment variables */
3103 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3104 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3105 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3106 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3107 /* Add server environment variables */
3108 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3109 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3110 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3111 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3112 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3113 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003114
Christopher Faulet61cc8522020-04-20 14:54:42 +02003115 /* Ensure that we don't leave any hole in check->envp */
3116 for (i = 0; i < EXTCHK_SIZE; i++)
3117 if (!check->envp[i])
3118 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003119
Christopher Faulet61cc8522020-04-20 14:54:42 +02003120 return 1;
3121err:
3122 if (check->envp) {
3123 for (i = 0; i < EXTCHK_SIZE; i++)
3124 free(check->envp[i]);
3125 free(check->envp);
3126 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003127 }
3128
Christopher Faulet61cc8522020-04-20 14:54:42 +02003129 if (check->argv) {
3130 for (i = 1; i < 5; i++)
3131 free(check->argv[i]);
3132 free(check->argv);
3133 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003134 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003135 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003136}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003137
Christopher Faulet61cc8522020-04-20 14:54:42 +02003138/*
3139 * establish a server health-check that makes use of a process.
3140 *
3141 * It can return one of :
3142 * - SF_ERR_NONE if everything's OK
3143 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3144 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3145 *
3146 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003147 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003148static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003149{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003150 char buf[256];
3151 struct check *check = t->context;
3152 struct server *s = check->server;
3153 struct proxy *px = s->proxy;
3154 int status;
3155 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003156
Christopher Faulet61cc8522020-04-20 14:54:42 +02003157 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003158
Christopher Faulet61cc8522020-04-20 14:54:42 +02003159 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003160
Christopher Faulet61cc8522020-04-20 14:54:42 +02003161 pid = fork();
3162 if (pid < 0) {
3163 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3164 (global.tune.options & GTUNE_INSECURE_FORK) ?
3165 "" : " (likely caused by missing 'insecure-fork-wanted')",
3166 strerror(errno));
3167 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003168 goto out;
3169 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003170 if (pid == 0) {
3171 /* Child */
3172 extern char **environ;
3173 struct rlimit limit;
3174 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003175
Christopher Faulet61cc8522020-04-20 14:54:42 +02003176 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3177 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003178
Christopher Faulet61cc8522020-04-20 14:54:42 +02003179 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003180
Christopher Faulet61cc8522020-04-20 14:54:42 +02003181 /* restore the initial FD limits */
3182 limit.rlim_cur = rlim_fd_cur_at_boot;
3183 limit.rlim_max = rlim_fd_max_at_boot;
3184 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3185 getrlimit(RLIMIT_NOFILE, &limit);
3186 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3187 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3188 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3189 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003190
Christopher Faulet61cc8522020-04-20 14:54:42 +02003191 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003192
Christopher Faulet61cc8522020-04-20 14:54:42 +02003193 /* Update some environment variables and command args: curconn, server addr and server port */
3194 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003195
Christopher Faulet61cc8522020-04-20 14:54:42 +02003196 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3197 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003198
Christopher Faulet61cc8522020-04-20 14:54:42 +02003199 *check->argv[4] = 0;
3200 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3201 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3202 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003203
Christopher Faulet61cc8522020-04-20 14:54:42 +02003204 haproxy_unblock_signals();
3205 execvp(px->check_command, check->argv);
3206 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3207 strerror(errno));
3208 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003209 }
3210
Christopher Faulet61cc8522020-04-20 14:54:42 +02003211 /* Parent */
3212 if (check->result == CHK_RES_UNKNOWN) {
3213 if (pid_list_add(pid, t) != NULL) {
3214 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3215
3216 if (px->timeout.check && px->timeout.connect) {
3217 int t_con = tick_add(now_ms, px->timeout.connect);
3218 t->expire = tick_first(t->expire, t_con);
3219 }
3220 status = SF_ERR_NONE;
3221 goto out;
3222 }
3223 else {
3224 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3225 }
3226 kill(pid, SIGTERM); /* process creation error */
3227 }
3228 else
3229 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3230
3231out:
3232 unblock_sigchld();
3233 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003234}
3235
Christopher Faulet61cc8522020-04-20 14:54:42 +02003236/*
3237 * manages a server health-check that uses an external process. Returns
3238 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003239 *
3240 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003241 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003242 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003243static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003244{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003245 struct check *check = context;
3246 struct server *s = check->server;
3247 int rv;
3248 int ret;
3249 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003250
Christopher Faulet61cc8522020-04-20 14:54:42 +02003251 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3252 if (!(check->state & CHK_ST_INPROGRESS)) {
3253 /* no check currently running */
3254 if (!expired) /* woke up too early */
3255 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003256
Christopher Faulet61cc8522020-04-20 14:54:42 +02003257 /* we don't send any health-checks when the proxy is
3258 * stopped, the server should not be checked or the check
3259 * is disabled.
3260 */
3261 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3262 s->proxy->state == PR_STSTOPPED)
3263 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003264
Christopher Faulet61cc8522020-04-20 14:54:42 +02003265 /* we'll initiate a new check */
3266 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003267
Christopher Faulet61cc8522020-04-20 14:54:42 +02003268 check->state |= CHK_ST_INPROGRESS;
3269
3270 ret = connect_proc_chk(t);
3271 if (ret == SF_ERR_NONE) {
3272 /* the process was forked, we allow up to min(inter,
3273 * timeout.connect) for it to report its status, but
3274 * only when timeout.check is set as it may be to short
3275 * for a full check otherwise.
3276 */
3277 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3278
3279 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3280 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3281 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003282 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003283 task_set_affinity(t, tid_bit);
3284 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003285 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003286
Christopher Faulet61cc8522020-04-20 14:54:42 +02003287 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003288
Christopher Faulet61cc8522020-04-20 14:54:42 +02003289 check->state &= ~CHK_ST_INPROGRESS;
3290 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003291
Christopher Faulet61cc8522020-04-20 14:54:42 +02003292 /* we allow up to min(inter, timeout.connect) for a connection
3293 * to establish but only when timeout.check is set
3294 * as it may be to short for a full check otherwise
3295 */
3296 while (tick_is_expired(t->expire, now_ms)) {
3297 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003298
Christopher Faulet61cc8522020-04-20 14:54:42 +02003299 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3300 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003301
Christopher Faulet61cc8522020-04-20 14:54:42 +02003302 if (s->proxy->timeout.check)
3303 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003304 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003305 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003306 else {
3307 /* there was a test running.
3308 * First, let's check whether there was an uncaught error,
3309 * which can happen on connect timeout or error.
3310 */
3311 if (check->result == CHK_RES_UNKNOWN) {
3312 /* good connection is enough for pure TCP check */
3313 struct pid_list *elem = check->curpid;
3314 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003315
Christopher Faulet61cc8522020-04-20 14:54:42 +02003316 if (elem->exited) {
3317 status = elem->status; /* Save in case the process exits between use below */
3318 if (!WIFEXITED(status))
3319 check->code = -1;
3320 else
3321 check->code = WEXITSTATUS(status);
3322 if (!WIFEXITED(status) || WEXITSTATUS(status))
3323 status = HCHK_STATUS_PROCERR;
3324 else
3325 status = HCHK_STATUS_PROCOK;
3326 } else if (expired) {
3327 status = HCHK_STATUS_PROCTOUT;
3328 ha_warning("kill %d\n", (int)elem->pid);
3329 kill(elem->pid, SIGTERM);
3330 }
3331 set_server_check_status(check, status, NULL);
3332 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003333
Christopher Faulet61cc8522020-04-20 14:54:42 +02003334 if (check->result == CHK_RES_FAILED) {
3335 /* a failure or timeout detected */
3336 check_notify_failure(check);
3337 }
3338 else if (check->result == CHK_RES_CONDPASS) {
3339 /* check is OK but asks for stopping mode */
3340 check_notify_stopping(check);
3341 }
3342 else if (check->result == CHK_RES_PASSED) {
3343 /* a success was detected */
3344 check_notify_success(check);
3345 }
3346 task_set_affinity(t, 1);
3347 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003348
Christopher Faulet61cc8522020-04-20 14:54:42 +02003349 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003350
Christopher Faulet61cc8522020-04-20 14:54:42 +02003351 rv = 0;
3352 if (global.spread_checks > 0) {
3353 rv = srv_getinter(check) * global.spread_checks / 100;
3354 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3355 }
3356 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3357 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003358
Christopher Faulet61cc8522020-04-20 14:54:42 +02003359 reschedule:
3360 while (tick_is_expired(t->expire, now_ms))
3361 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003362
Christopher Faulet61cc8522020-04-20 14:54:42 +02003363 out_unlock:
3364 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3365 return t;
3366}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003367
Baptiste Assmann248f1172018-03-01 21:49:01 +01003368
Christopher Faulet61cc8522020-04-20 14:54:42 +02003369/**************************************************************************/
3370/***************** Health-checks based on connections *********************/
3371/**************************************************************************/
3372/* This function is used only for server health-checks. It handles connection
3373 * status updates including errors. If necessary, it wakes the check task up.
3374 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3375 * connection (eg: reconnect). It relies on tcpcheck_main().
3376 */
3377static int wake_srv_chk(struct conn_stream *cs)
3378{
3379 struct connection *conn = cs->conn;
3380 struct check *check = cs->data;
3381 struct email_alertq *q = container_of(check, typeof(*q), check);
3382 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003383
Christopher Faulet61cc8522020-04-20 14:54:42 +02003384 if (check->server)
3385 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3386 else
3387 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003388
Christopher Faulet61cc8522020-04-20 14:54:42 +02003389 /* we may have to make progress on the TCP checks */
3390 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003391
Christopher Faulet61cc8522020-04-20 14:54:42 +02003392 cs = check->cs;
3393 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003394
Christopher Faulet61cc8522020-04-20 14:54:42 +02003395 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3396 /* We may get error reports bypassing the I/O handlers, typically
3397 * the case when sending a pure TCP check which fails, then the I/O
3398 * handlers above are not called. This is completely handled by the
3399 * main processing task so let's simply wake it up. If we get here,
3400 * we expect errno to still be valid.
3401 */
3402 chk_report_conn_err(check, errno, 0);
3403 task_wakeup(check->task, TASK_WOKEN_IO);
3404 }
3405
3406 if (check->result != CHK_RES_UNKNOWN) {
3407 /* Check complete or aborted. If connection not yet closed do it
3408 * now and wake the check task up to be sure the result is
3409 * handled ASAP. */
3410 conn_sock_drain(conn);
3411 cs_close(cs);
3412 ret = -1;
3413 /* We may have been scheduled to run, and the
3414 * I/O handler expects to have a cs, so remove
3415 * the tasklet
3416 */
3417 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3418 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003419 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003420
3421 if (check->server)
3422 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003423 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003424 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003425
Christopher Faulet61cc8522020-04-20 14:54:42 +02003426 /* if a connection got replaced, we must absolutely prevent the connection
3427 * handler from touching its fd, and perform the FD polling updates ourselves
3428 */
3429 if (ret < 0)
3430 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003431
Christopher Faulet61cc8522020-04-20 14:54:42 +02003432 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003433}
3434
Christopher Faulet61cc8522020-04-20 14:54:42 +02003435/* This function checks if any I/O is wanted, and if so, attempts to do so */
3436static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003437{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003438 struct check *check = ctx;
3439 struct conn_stream *cs = check->cs;
3440 struct email_alertq *q = container_of(check, typeof(*q), check);
3441 int ret = 0;
Simon Hormanb1900d52015-01-30 11:22:54 +09003442
Christopher Faulet61cc8522020-04-20 14:54:42 +02003443 if (!(check->wait_list.events & SUB_RETRY_SEND))
3444 ret = wake_srv_chk(cs);
3445 if (ret == 0 && !(check->wait_list.events & SUB_RETRY_RECV)) {
3446 if (check->server)
3447 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3448 else
3449 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Hormanb1900d52015-01-30 11:22:54 +09003450
Christopher Faulet61cc8522020-04-20 14:54:42 +02003451 if (unlikely(check->result == CHK_RES_FAILED)) {
3452 /* collect possible new errors */
3453 if (cs->conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
3454 chk_report_conn_err(check, 0, 0);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003455
Christopher Faulet61cc8522020-04-20 14:54:42 +02003456 /* Reset the check buffer... */
3457 b_reset(&check->bi);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003458
Christopher Faulet61cc8522020-04-20 14:54:42 +02003459 /* Close the connection... We still attempt to nicely close if,
3460 * for instance, SSL needs to send a "close notify." Later, we perform
3461 * a hard close and reset the connection if some data are pending,
3462 * otherwise we end up with many TIME_WAITs and eat all the source port
3463 * range quickly. To avoid sending RSTs all the time, we first try to
3464 * drain pending data.
3465 */
3466 /* Call cs_shutr() first, to add the CO_FL_SOCK_RD_SH flag on the
3467 * connection, to make sure cs_shutw() will not lead to a shutdown()
3468 * that would provoke TIME_WAITs.
3469 */
3470 cs_shutr(cs, CS_SHR_DRAIN);
3471 cs_shutw(cs, CS_SHW_NORMAL);
Simon Hormanb1900d52015-01-30 11:22:54 +09003472
Christopher Faulet61cc8522020-04-20 14:54:42 +02003473 /* OK, let's not stay here forever */
3474 if (check->result == CHK_RES_FAILED)
3475 cs->conn->flags |= CO_FL_ERROR;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01003476
Christopher Faulet61cc8522020-04-20 14:54:42 +02003477 task_wakeup(t, TASK_WOKEN_IO);
3478 }
3479
3480 if (check->server)
3481 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3482 else
3483 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet23d86d12018-01-25 11:36:35 +01003484 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003485 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003486}
3487
Christopher Faulet61cc8522020-04-20 14:54:42 +02003488/* manages a server health-check that uses a connection. Returns
3489 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3490 *
3491 * Please do NOT place any return statement in this function and only leave
3492 * via the out_unlock label.
3493 */
3494static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003495{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003496 struct check *check = context;
3497 struct proxy *proxy = check->proxy;
3498 struct conn_stream *cs = check->cs;
3499 struct connection *conn = cs_conn(cs);
3500 int rv;
3501 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003502
Christopher Faulet61cc8522020-04-20 14:54:42 +02003503 if (check->server)
3504 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3505 if (!(check->state & CHK_ST_INPROGRESS)) {
3506 /* no check currently running */
3507 if (!expired) /* woke up too early */
3508 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003509
Christopher Faulet61cc8522020-04-20 14:54:42 +02003510 /* we don't send any health-checks when the proxy is
3511 * stopped, the server should not be checked or the check
3512 * is disabled.
3513 */
3514 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3515 proxy->state == PR_STSTOPPED)
3516 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003517
Christopher Faulet61cc8522020-04-20 14:54:42 +02003518 /* we'll initiate a new check */
3519 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003520
Christopher Faulet61cc8522020-04-20 14:54:42 +02003521 check->state |= CHK_ST_INPROGRESS;
3522 b_reset(&check->bi);
3523 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003524
Christopher Faulet61cc8522020-04-20 14:54:42 +02003525 task_set_affinity(t, tid_bit);
3526 cs = check->cs;
3527 conn = cs_conn(cs);
3528 if (!conn) {
3529 check->current_step = NULL;
3530 tcpcheck_main(check);
3531 goto out_unlock;
3532 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003533
Christopher Faulet61cc8522020-04-20 14:54:42 +02003534 conn->flags |= CO_FL_ERROR;
3535 chk_report_conn_err(check, 0, 0);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003536
Christopher Faulet61cc8522020-04-20 14:54:42 +02003537 /* here, we have seen a synchronous error, no fd was allocated */
3538 task_set_affinity(t, MAX_THREADS_MASK);
3539 if (cs) {
3540 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003541 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003542 /* We may have been scheduled to run, and the
3543 * I/O handler expects to have a cs, so remove
3544 * the tasklet
3545 */
3546 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3547 cs_destroy(cs);
3548 cs = check->cs = NULL;
3549 conn = NULL;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003550 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003551
Christopher Faulet61cc8522020-04-20 14:54:42 +02003552 check->state &= ~CHK_ST_INPROGRESS;
3553 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003554
Christopher Faulet61cc8522020-04-20 14:54:42 +02003555 /* we allow up to min(inter, timeout.connect) for a connection
3556 * to establish but only when timeout.check is set
3557 * as it may be to short for a full check otherwise
3558 */
3559 while (tick_is_expired(t->expire, now_ms)) {
3560 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003561
Christopher Faulet61cc8522020-04-20 14:54:42 +02003562 t_con = tick_add(t->expire, proxy->timeout.connect);
3563 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3564 if (proxy->timeout.check)
3565 t->expire = tick_first(t->expire, t_con);
3566 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003567 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003568 else {
3569 /* there was a test running.
3570 * First, let's check whether there was an uncaught error,
3571 * which can happen on connect timeout or error.
3572 */
3573 if (check->result == CHK_RES_UNKNOWN) {
3574 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3575 chk_report_conn_err(check, 0, expired);
3576 }
3577 else
3578 goto out_unlock; /* timeout not reached, wait again */
3579 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003580
Christopher Faulet61cc8522020-04-20 14:54:42 +02003581 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003582
Christopher Faulet61cc8522020-04-20 14:54:42 +02003583 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003584
Christopher Faulet61cc8522020-04-20 14:54:42 +02003585 if (conn && conn->xprt) {
3586 /* The check was aborted and the connection was not yet closed.
3587 * This can happen upon timeout, or when an external event such
3588 * as a failed response coupled with "observe layer7" caused the
3589 * server state to be suddenly changed.
3590 */
3591 conn_sock_drain(conn);
3592 cs_close(cs);
3593 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003594
Christopher Faulet61cc8522020-04-20 14:54:42 +02003595 if (cs) {
3596 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003597 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003598 /* We may have been scheduled to run, and the
3599 * I/O handler expects to have a cs, so remove
3600 * the tasklet
3601 */
3602 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3603 cs_destroy(cs);
3604 cs = check->cs = NULL;
3605 conn = NULL;
3606 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003607
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003608 if (check->sess != NULL) {
3609 vars_prune(&check->vars, check->sess, NULL);
3610 session_free(check->sess);
3611 check->sess = NULL;
3612 }
3613
Christopher Faulet61cc8522020-04-20 14:54:42 +02003614 if (check->server) {
3615 if (check->result == CHK_RES_FAILED) {
3616 /* a failure or timeout detected */
3617 check_notify_failure(check);
3618 }
3619 else if (check->result == CHK_RES_CONDPASS) {
3620 /* check is OK but asks for stopping mode */
3621 check_notify_stopping(check);
3622 }
3623 else if (check->result == CHK_RES_PASSED) {
3624 /* a success was detected */
3625 check_notify_success(check);
3626 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003627 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003628 task_set_affinity(t, MAX_THREADS_MASK);
3629 check->state &= ~CHK_ST_INPROGRESS;
3630
3631 if (check->server) {
3632 rv = 0;
3633 if (global.spread_checks > 0) {
3634 rv = srv_getinter(check) * global.spread_checks / 100;
3635 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3636 }
3637 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003638 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003639 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003640
Christopher Faulet61cc8522020-04-20 14:54:42 +02003641 reschedule:
3642 while (tick_is_expired(t->expire, now_ms))
3643 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3644 out_unlock:
3645 if (check->server)
3646 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3647 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003648}
3649
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003650
Christopher Faulet61cc8522020-04-20 14:54:42 +02003651/**************************************************************************/
3652/******************* Internals to parse tcp-check rules *******************/
3653/**************************************************************************/
3654struct action_kw_list tcp_check_keywords = {
3655 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3656};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003657
Christopher Faulet61cc8522020-04-20 14:54:42 +02003658/* Return the struct action_kw associated to a keyword */
3659static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003660{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003661 return action_lookup(&tcp_check_keywords.list, kw);
3662}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003663
Christopher Faulet61cc8522020-04-20 14:54:42 +02003664static void action_kw_tcp_check_build_list(struct buffer *chk)
3665{
3666 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003667}
3668
Christopher Faulet61cc8522020-04-20 14:54:42 +02003669/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3670 * returned on error.
3671 */
3672static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3673 struct list *rules, struct action_kw *kw,
3674 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003675{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003676 struct tcpcheck_rule *chk = NULL;
3677 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003678
Christopher Faulet61cc8522020-04-20 14:54:42 +02003679 actrule = calloc(1, sizeof(*actrule));
3680 if (!actrule) {
3681 memprintf(errmsg, "out of memory");
3682 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003683 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003684 actrule->kw = kw;
3685 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003686
Christopher Faulet61cc8522020-04-20 14:54:42 +02003687 cur_arg++;
3688 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3689 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3690 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003691 }
3692
Christopher Faulet61cc8522020-04-20 14:54:42 +02003693 chk = calloc(1, sizeof(*chk));
3694 if (!chk) {
3695 memprintf(errmsg, "out of memory");
3696 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003697 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003698 chk->action = TCPCHK_ACT_ACTION_KW;
3699 chk->action_kw.rule = actrule;
3700 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003701
3702 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003703 free(actrule);
3704 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003705}
3706
Christopher Faulet61cc8522020-04-20 14:54:42 +02003707/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3708 * returned on error.
3709 */
3710static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3711 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003712{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003713 struct tcpcheck_rule *chk = NULL;
3714 struct sockaddr_storage *sk = NULL;
3715 char *comment = NULL, *sni = NULL, *alpn = NULL;
3716 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003717 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003718 unsigned short conn_opts = 0;
3719 long port = 0;
3720 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003721
Christopher Faulet61cc8522020-04-20 14:54:42 +02003722 list_for_each_entry(chk, rules, list) {
3723 if (chk->action == TCPCHK_ACT_CONNECT)
3724 break;
3725 if (chk->action == TCPCHK_ACT_COMMENT ||
3726 chk->action == TCPCHK_ACT_ACTION_KW ||
3727 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3728 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003729
Christopher Faulet61cc8522020-04-20 14:54:42 +02003730 memprintf(errmsg, "first step MUST also be a 'connect', "
3731 "optionnaly preceded by a 'set-var', an 'unset-var' or a 'comment', "
3732 "when there is a 'connect' step in the tcp-check ruleset");
3733 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003734 }
3735
Christopher Faulet61cc8522020-04-20 14:54:42 +02003736 cur_arg++;
3737 while (*(args[cur_arg])) {
3738 if (strcmp(args[cur_arg], "default") == 0)
3739 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3740 else if (strcmp(args[cur_arg], "addr") == 0) {
3741 int port1, port2;
3742 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003743
Christopher Faulet61cc8522020-04-20 14:54:42 +02003744 if (!*(args[cur_arg+1])) {
3745 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3746 goto error;
3747 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003748
Christopher Faulet61cc8522020-04-20 14:54:42 +02003749 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3750 if (!sk) {
3751 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3752 goto error;
3753 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003754
Christopher Faulet61cc8522020-04-20 14:54:42 +02003755 proto = protocol_by_family(sk->ss_family);
3756 if (!proto || !proto->connect) {
3757 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3758 args[cur_arg]);
3759 goto error;
3760 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003761
Christopher Faulet61cc8522020-04-20 14:54:42 +02003762 if (port1 != port2) {
3763 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3764 args[cur_arg], args[cur_arg+1]);
3765 goto error;
3766 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003767
Christopher Faulet61cc8522020-04-20 14:54:42 +02003768 cur_arg++;
3769 }
3770 else if (strcmp(args[cur_arg], "port") == 0) {
3771 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003772
Christopher Faulet61cc8522020-04-20 14:54:42 +02003773 if (!*(args[cur_arg+1])) {
3774 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3775 goto error;
3776 }
3777 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003778
Christopher Faulet61cc8522020-04-20 14:54:42 +02003779 port = 0;
3780 release_sample_expr(port_expr);
3781 p = args[cur_arg]; end = p + strlen(p);
3782 port = read_uint(&p, end);
3783 if (p != end) {
3784 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003785
Christopher Faulet61cc8522020-04-20 14:54:42 +02003786 px->conf.args.ctx = ARGC_SRV;
3787 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3788 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003789
Christopher Faulet61cc8522020-04-20 14:54:42 +02003790 if (!port_expr) {
3791 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3792 goto error;
3793 }
3794 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3795 memprintf(errmsg, "error detected while parsing port expression : "
3796 " fetch method '%s' extracts information from '%s', "
3797 "none of which is available here.\n",
3798 args[cur_arg], sample_src_names(port_expr->fetch->use));
3799 goto error;
3800 }
3801 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3802 }
3803 else if (port > 65535 || port < 1) {
3804 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3805 args[cur_arg]);
3806 goto error;
3807 }
3808 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003809 else if (strcmp(args[cur_arg], "proto") == 0) {
3810 if (!*(args[cur_arg+1])) {
3811 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3812 goto error;
3813 }
3814 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3815 if (!mux_proto) {
3816 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3817 goto error;
3818 }
3819 cur_arg++;
3820 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003821 else if (strcmp(args[cur_arg], "comment") == 0) {
3822 if (!*(args[cur_arg+1])) {
3823 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3824 goto error;
3825 }
3826 cur_arg++;
3827 free(comment);
3828 comment = strdup(args[cur_arg]);
3829 if (!comment) {
3830 memprintf(errmsg, "out of memory");
3831 goto error;
3832 }
3833 }
3834 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3835 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3836 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3837 conn_opts |= TCPCHK_OPT_SOCKS4;
3838 else if (strcmp(args[cur_arg], "linger") == 0)
3839 conn_opts |= TCPCHK_OPT_LINGER;
3840#ifdef USE_OPENSSL
3841 else if (strcmp(args[cur_arg], "ssl") == 0) {
3842 px->options |= PR_O_TCPCHK_SSL;
3843 conn_opts |= TCPCHK_OPT_SSL;
3844 }
3845 else if (strcmp(args[cur_arg], "sni") == 0) {
3846 if (!*(args[cur_arg+1])) {
3847 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3848 goto error;
3849 }
3850 cur_arg++;
3851 free(sni);
3852 sni = strdup(args[cur_arg]);
3853 if (!sni) {
3854 memprintf(errmsg, "out of memory");
3855 goto error;
3856 }
3857 }
3858 else if (strcmp(args[cur_arg], "alpn") == 0) {
3859#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3860 free(alpn);
3861 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3862 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3863 goto error;
3864 }
3865 cur_arg++;
3866#else
3867 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003868 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003869#endif
3870 }
3871#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003872
Christopher Faulet61cc8522020-04-20 14:54:42 +02003873 else {
3874 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3875#ifdef USE_OPENSSL
3876 ", 'ssl', 'sni', 'alpn'"
3877#endif /* USE_OPENSSL */
3878 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3879 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003880 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003881 }
3882 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003883 }
3884
Christopher Faulet61cc8522020-04-20 14:54:42 +02003885 chk = calloc(1, sizeof(*chk));
3886 if (!chk) {
3887 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003888 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003889 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003890 chk->action = TCPCHK_ACT_CONNECT;
3891 chk->comment = comment;
3892 chk->connect.port = port;
3893 chk->connect.options = conn_opts;
3894 chk->connect.sni = sni;
3895 chk->connect.alpn = alpn;
3896 chk->connect.alpn_len= alpn_len;
3897 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003898 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003899 if (sk)
3900 chk->connect.addr = *sk;
3901 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003902
Christopher Faulet61cc8522020-04-20 14:54:42 +02003903 error:
3904 free(alpn);
3905 free(sni);
3906 free(comment);
3907 release_sample_expr(port_expr);
3908 return NULL;
3909}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003910
Christopher Faulet61cc8522020-04-20 14:54:42 +02003911/* Parses and creates a tcp-check send rule. NULL is returned on error */
3912static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3913 const char *file, int line, char **errmsg)
3914{
3915 struct tcpcheck_rule *chk = NULL;
3916 char *comment = NULL, *data = NULL;
3917 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003918
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003919 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3920 type = TCPCHK_SEND_BINARY_LF;
3921 else if (strcmp(args[cur_arg], "send-binary") == 0)
3922 type = TCPCHK_SEND_BINARY;
3923 else if (strcmp(args[cur_arg], "send-lf") == 0)
3924 type = TCPCHK_SEND_STRING_LF;
3925 else if (strcmp(args[cur_arg], "send") == 0)
3926 type = TCPCHK_SEND_STRING;
3927
Christopher Faulet61cc8522020-04-20 14:54:42 +02003928 if (!*(args[cur_arg+1])) {
3929 memprintf(errmsg, "'%s' expects a %s as argument",
3930 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003931 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003932 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003933
Christopher Faulet61cc8522020-04-20 14:54:42 +02003934 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003935
Christopher Faulet61cc8522020-04-20 14:54:42 +02003936 cur_arg += 2;
3937 while (*(args[cur_arg])) {
3938 if (strcmp(args[cur_arg], "comment") == 0) {
3939 if (!*(args[cur_arg+1])) {
3940 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3941 goto error;
3942 }
3943 cur_arg++;
3944 free(comment);
3945 comment = strdup(args[cur_arg]);
3946 if (!comment) {
3947 memprintf(errmsg, "out of memory");
3948 goto error;
3949 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003950 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003951 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003952 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003953 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003954 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003955 }
3956 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003957 }
3958
Christopher Faulet61cc8522020-04-20 14:54:42 +02003959 chk = calloc(1, sizeof(*chk));
3960 if (!chk) {
3961 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003962 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003963 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003964 chk->action = TCPCHK_ACT_SEND;
3965 chk->comment = comment;
3966 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003967
Christopher Faulet61cc8522020-04-20 14:54:42 +02003968 switch (chk->send.type) {
3969 case TCPCHK_SEND_STRING:
3970 chk->send.data = ist2(strdup(data), strlen(data));
3971 if (!isttest(chk->send.data)) {
3972 memprintf(errmsg, "out of memory");
3973 goto error;
3974 }
3975 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003976 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003977 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003978 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003979 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3980 goto error;
3981 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003982 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003983 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003984 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003985 case TCPCHK_SEND_STRING_LF:
3986 case TCPCHK_SEND_BINARY_LF:
3987 LIST_INIT(&chk->send.fmt);
3988 px->conf.args.ctx = ARGC_SRV;
3989 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3990 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3991 goto error;
3992 }
3993 break;
3994 case TCPCHK_SEND_HTTP:
3995 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003996 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003997 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003998
Christopher Faulet61cc8522020-04-20 14:54:42 +02003999 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004000
Christopher Faulet61cc8522020-04-20 14:54:42 +02004001 error:
4002 free(chk);
4003 free(comment);
4004 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004005}
4006
Christopher Faulet61cc8522020-04-20 14:54:42 +02004007/* Parses and creates a http-check send rule. NULL is returned on error */
4008static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
4009 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004010{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004011 struct tcpcheck_rule *chk = NULL;
4012 struct tcpcheck_http_hdr *hdr = NULL;
4013 struct http_hdr hdrs[global.tune.max_http_hdr];
4014 char *meth = NULL, *uri = NULL, *vsn = NULL;
4015 char *body = NULL, *comment = NULL;
4016 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004017 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004018
4019 cur_arg++;
4020 while (*(args[cur_arg])) {
4021 if (strcmp(args[cur_arg], "meth") == 0) {
4022 if (!*(args[cur_arg+1])) {
4023 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4024 goto error;
4025 }
4026 cur_arg++;
4027 meth = args[cur_arg];
4028 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004029 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 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 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004034 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4035 if (strcmp(args[cur_arg], "uri-lf") == 0)
4036 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004037 cur_arg++;
4038 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004039 }
Christopher Faulet907701b2020-04-28 09:37:00 +02004040 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004041 if (!*(args[cur_arg+1])) {
4042 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4043 goto error;
4044 }
4045 cur_arg++;
4046 vsn = args[cur_arg];
4047 }
4048 else if (strcmp(args[cur_arg], "hdr") == 0) {
4049 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4050 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4051 goto error;
4052 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004053
4054 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4055 if (host_hdr >= 0) {
4056 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4057 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4058 goto error;
4059 }
4060 host_hdr = i;
4061 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004062 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4063 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4064 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4065 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004066
Christopher Faulet61cc8522020-04-20 14:54:42 +02004067 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4068 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4069 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004070 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004071 cur_arg += 2;
4072 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004073 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004074 if (!*(args[cur_arg+1])) {
4075 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4076 goto error;
4077 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004078 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4079 if (strcmp(args[cur_arg], "body-lf") == 0)
4080 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004081 cur_arg++;
4082 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004083 }
4084 else if (strcmp(args[cur_arg], "comment") == 0) {
4085 if (!*(args[cur_arg+1])) {
4086 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4087 goto error;
4088 }
4089 cur_arg++;
4090 free(comment);
4091 comment = strdup(args[cur_arg]);
4092 if (!comment) {
4093 memprintf(errmsg, "out of memory");
4094 goto error;
4095 }
4096 }
4097 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004098 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004099 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004100 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004101 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004102 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004103 }
4104
Christopher Faulet61cc8522020-04-20 14:54:42 +02004105 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004106
Christopher Faulet61cc8522020-04-20 14:54:42 +02004107 chk = calloc(1, sizeof(*chk));
4108 if (!chk) {
4109 memprintf(errmsg, "out of memory");
4110 goto error;
4111 }
4112 chk->action = TCPCHK_ACT_SEND;
4113 chk->comment = comment; comment = NULL;
4114 chk->send.type = TCPCHK_SEND_HTTP;
4115 chk->send.http.flags = flags;
4116 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004117
Christopher Faulet61cc8522020-04-20 14:54:42 +02004118 if (meth) {
4119 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4120 chk->send.http.meth.str.area = strdup(meth);
4121 chk->send.http.meth.str.data = strlen(meth);
4122 if (!chk->send.http.meth.str.area) {
4123 memprintf(errmsg, "out of memory");
4124 goto error;
4125 }
4126 }
4127 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004128 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4129 LIST_INIT(&chk->send.http.uri_fmt);
4130 px->conf.args.ctx = ARGC_SRV;
4131 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4132 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4133 goto error;
4134 }
4135 }
4136 else {
4137 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4138 if (!isttest(chk->send.http.uri)) {
4139 memprintf(errmsg, "out of memory");
4140 goto error;
4141 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004142 }
4143 }
4144 if (vsn) {
4145 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4146 if (!isttest(chk->send.http.vsn)) {
4147 memprintf(errmsg, "out of memory");
4148 goto error;
4149 }
4150 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004151 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004152 hdr = calloc(1, sizeof(*hdr));
4153 if (!hdr) {
4154 memprintf(errmsg, "out of memory");
4155 goto error;
4156 }
4157 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004158 hdr->name = istdup(hdrs[i].n);
4159 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004160 memprintf(errmsg, "out of memory");
4161 goto error;
4162 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004163
Christopher Fauletb61caf42020-04-21 10:57:42 +02004164 ist0(hdrs[i].v);
4165 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 +02004166 goto error;
4167 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4168 hdr = NULL;
4169 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004170
Christopher Faulet61cc8522020-04-20 14:54:42 +02004171 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004172 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4173 LIST_INIT(&chk->send.http.body_fmt);
4174 px->conf.args.ctx = ARGC_SRV;
4175 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4176 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4177 goto error;
4178 }
4179 }
4180 else {
4181 chk->send.http.body = ist2(strdup(body), strlen(body));
4182 if (!isttest(chk->send.http.body)) {
4183 memprintf(errmsg, "out of memory");
4184 goto error;
4185 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004186 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004187 }
4188
Christopher Faulet61cc8522020-04-20 14:54:42 +02004189 return chk;
4190
4191 error:
4192 free_tcpcheck_http_hdr(hdr);
4193 free_tcpcheck(chk, 0);
4194 free(comment);
4195 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004196}
4197
Christopher Faulet61cc8522020-04-20 14:54:42 +02004198/* Parses and creates a http-check comment rule. NULL is returned on error */
4199static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4200 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004201{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004202 struct tcpcheck_rule *chk = NULL;
4203 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004204
Christopher Faulet61cc8522020-04-20 14:54:42 +02004205 if (!*(args[cur_arg+1])) {
4206 memprintf(errmsg, "expects a string as argument");
4207 goto error;
4208 }
4209 cur_arg++;
4210 comment = strdup(args[cur_arg]);
4211 if (!comment) {
4212 memprintf(errmsg, "out of memory");
4213 goto error;
4214 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004215
Christopher Faulet61cc8522020-04-20 14:54:42 +02004216 chk = calloc(1, sizeof(*chk));
4217 if (!chk) {
4218 memprintf(errmsg, "out of memory");
4219 goto error;
4220 }
4221 chk->action = TCPCHK_ACT_COMMENT;
4222 chk->comment = comment;
4223 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004224
Christopher Faulet61cc8522020-04-20 14:54:42 +02004225 error:
4226 free(comment);
4227 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004228}
4229
Christopher Faulet61cc8522020-04-20 14:54:42 +02004230/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4231 * on error. <proto> is set to the right protocol flags (covered by the
4232 * TCPCHK_RULES_PROTO_CHK mask).
4233 */
4234static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4235 struct list *rules, unsigned int proto,
4236 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004237{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004238 struct tcpcheck_rule *prev_check, *chk = NULL;
4239 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004240 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004241 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004242 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4243 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4244 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004245 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004246 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004247 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004248
Christopher Faulet39708192020-05-05 10:47:36 +02004249 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004250 if (!*(args[cur_arg+1])) {
4251 memprintf(errmsg, "expects at least a matching pattern as arguments");
4252 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004253 }
4254
Christopher Faulet61cc8522020-04-20 14:54:42 +02004255 cur_arg++;
4256 while (*(args[cur_arg])) {
4257 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004258
Christopher Faulet61cc8522020-04-20 14:54:42 +02004259 rescan:
4260 if (strcmp(args[cur_arg], "min-recv") == 0) {
4261 if (in_pattern) {
4262 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4263 goto error;
4264 }
4265 if (!*(args[cur_arg+1])) {
4266 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4267 goto error;
4268 }
4269 /* Use an signed integer here because of chksize */
4270 cur_arg++;
4271 min_recv = atol(args[cur_arg]);
4272 if (min_recv < -1 || min_recv > INT_MAX) {
4273 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4274 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004275 }
4276 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004277 else if (*(args[cur_arg]) == '!') {
4278 in_pattern = 1;
4279 while (*(args[cur_arg]) == '!') {
4280 inverse = !inverse;
4281 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004282 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004283 if (!*(args[cur_arg]))
4284 cur_arg++;
4285 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004286 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004287 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4288 if (type != TCPCHK_EXPECT_UNDEF) {
4289 memprintf(errmsg, "only on pattern expected");
4290 goto error;
4291 }
4292 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004293 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004294 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004295 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004296
Christopher Faulet61cc8522020-04-20 14:54:42 +02004297 if (!*(args[cur_arg+1])) {
4298 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4299 goto error;
4300 }
4301 cur_arg++;
4302 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004303 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004304 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4305 if (proto == TCPCHK_RULES_HTTP_CHK)
4306 goto bad_http_kw;
4307 if (type != TCPCHK_EXPECT_UNDEF) {
4308 memprintf(errmsg, "only on pattern expected");
4309 goto error;
4310 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004311 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004312
Christopher Faulet61cc8522020-04-20 14:54:42 +02004313 if (!*(args[cur_arg+1])) {
4314 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4315 goto error;
4316 }
4317 cur_arg++;
4318 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004319 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004320 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4321 if (type != TCPCHK_EXPECT_UNDEF) {
4322 memprintf(errmsg, "only on pattern expected");
4323 goto error;
4324 }
4325 if (proto != TCPCHK_RULES_HTTP_CHK)
4326 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4327 else {
4328 if (*(args[cur_arg]) != 's')
4329 goto bad_http_kw;
4330 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4331 }
4332
4333 if (!*(args[cur_arg+1])) {
4334 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4335 goto error;
4336 }
4337 cur_arg++;
4338 pattern = args[cur_arg];
4339 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004340 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4341 if (proto != TCPCHK_RULES_HTTP_CHK)
4342 goto bad_tcp_kw;
4343 if (type != TCPCHK_EXPECT_UNDEF) {
4344 memprintf(errmsg, "only on pattern expected");
4345 goto error;
4346 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004347 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004348
Christopher Faulet61cc8522020-04-20 14:54:42 +02004349 if (!*(args[cur_arg+1])) {
4350 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4351 goto error;
4352 }
4353 cur_arg++;
4354 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004355 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004356 else if (strcmp(args[cur_arg], "custom") == 0) {
4357 if (in_pattern) {
4358 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4359 goto error;
4360 }
4361 if (type != TCPCHK_EXPECT_UNDEF) {
4362 memprintf(errmsg, "only on pattern expected");
4363 goto error;
4364 }
4365 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004366 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004367 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004368 int orig_arg = cur_arg;
4369
4370 if (proto != TCPCHK_RULES_HTTP_CHK)
4371 goto bad_tcp_kw;
4372 if (type != TCPCHK_EXPECT_UNDEF) {
4373 memprintf(errmsg, "only on pattern expected");
4374 goto error;
4375 }
4376 type = TCPCHK_EXPECT_HTTP_HEADER;
4377
Christopher Fauletb5594262020-05-05 20:23:13 +02004378 if (strcmp(args[cur_arg], "fhdr") == 0)
4379 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4380
Christopher Faulet39708192020-05-05 10:47:36 +02004381 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004382 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4383 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4384 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004385 args[orig_arg]);
4386 goto error;
4387 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004388
4389 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4390 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4391
Christopher Faulet39708192020-05-05 10:47:36 +02004392 cur_arg += 2;
4393 if (strcmp(args[cur_arg], "-m") == 0) {
4394 if (!*(args[cur_arg+1])) {
4395 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4396 args[orig_arg], args[cur_arg]);
4397 goto error;
4398 }
4399 if (strcmp(args[cur_arg+1], "str") == 0)
4400 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4401 else if (strcmp(args[cur_arg+1], "beg") == 0)
4402 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4403 else if (strcmp(args[cur_arg+1], "end") == 0)
4404 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4405 else if (strcmp(args[cur_arg+1], "sub") == 0)
4406 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004407 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4408 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4409 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4410 args[orig_arg]);
4411 goto error;
4412 }
Christopher Faulet39708192020-05-05 10:47:36 +02004413 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004414 }
Christopher Faulet39708192020-05-05 10:47:36 +02004415 else {
4416 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4417 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4418 goto error;
4419 }
4420 cur_arg += 2;
4421 }
4422 else
4423 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4424 npat = args[cur_arg];
4425
Christopher Fauletb5594262020-05-05 20:23:13 +02004426 if (!*(args[cur_arg+1]) ||
4427 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004428 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4429 goto next;
4430 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004431 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4432 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004433
4434 /* Parse the value pattern, optionnal */
Christopher Fauletb5594262020-05-05 20:23:13 +02004435 if (strcmp(args[cur_arg+2], "-m") == 0) {
4436 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004437 if (!*(args[cur_arg+1])) {
4438 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4439 args[orig_arg], args[cur_arg]);
4440 goto error;
4441 }
4442 if (strcmp(args[cur_arg+1], "str") == 0)
4443 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4444 else if (strcmp(args[cur_arg+1], "beg") == 0)
4445 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4446 else if (strcmp(args[cur_arg+1], "end") == 0)
4447 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4448 else if (strcmp(args[cur_arg+1], "sub") == 0)
4449 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004450 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4451 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4452 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4453 args[orig_arg]);
4454 goto error;
4455 }
Christopher Faulet39708192020-05-05 10:47:36 +02004456 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004457 }
Christopher Faulet39708192020-05-05 10:47:36 +02004458 else {
4459 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4460 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4461 goto error;
4462 }
Christopher Faulet39708192020-05-05 10:47:36 +02004463 }
4464 else
4465 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004466
Christopher Fauletb5594262020-05-05 20:23:13 +02004467 if (!*(args[cur_arg+2])) {
4468 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4469 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004470 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004471 vpat = args[cur_arg+2];
4472 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004473 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004474 else if (strcmp(args[cur_arg], "comment") == 0) {
4475 if (in_pattern) {
4476 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4477 goto error;
4478 }
4479 if (!*(args[cur_arg+1])) {
4480 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4481 goto error;
4482 }
4483 cur_arg++;
4484 free(comment);
4485 comment = strdup(args[cur_arg]);
4486 if (!comment) {
4487 memprintf(errmsg, "out of memory");
4488 goto error;
4489 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004490 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004491 else if (strcmp(args[cur_arg], "on-success") == 0) {
4492 if (in_pattern) {
4493 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4494 goto error;
4495 }
4496 if (!*(args[cur_arg+1])) {
4497 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4498 goto error;
4499 }
4500 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004501 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004502 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004503 else if (strcmp(args[cur_arg], "on-error") == 0) {
4504 if (in_pattern) {
4505 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4506 goto error;
4507 }
4508 if (!*(args[cur_arg+1])) {
4509 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4510 goto error;
4511 }
4512 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004513 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004514 }
4515 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4516 if (in_pattern) {
4517 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4518 goto error;
4519 }
4520 if (!*(args[cur_arg+1])) {
4521 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4522 goto error;
4523 }
4524 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4525 ok_st = HCHK_STATUS_L7OKD;
4526 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4527 ok_st = HCHK_STATUS_L7OKCD;
4528 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4529 ok_st = HCHK_STATUS_L6OK;
4530 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4531 ok_st = HCHK_STATUS_L4OK;
4532 else {
4533 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4534 args[cur_arg], args[cur_arg+1]);
4535 goto error;
4536 }
4537 cur_arg++;
4538 }
4539 else if (strcmp(args[cur_arg], "error-status") == 0) {
4540 if (in_pattern) {
4541 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4542 goto error;
4543 }
4544 if (!*(args[cur_arg+1])) {
4545 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4546 goto error;
4547 }
4548 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4549 err_st = HCHK_STATUS_L7RSP;
4550 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4551 err_st = HCHK_STATUS_L7STS;
4552 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4553 err_st = HCHK_STATUS_L6RSP;
4554 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4555 err_st = HCHK_STATUS_L4CON;
4556 else {
4557 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4558 args[cur_arg], args[cur_arg+1]);
4559 goto error;
4560 }
4561 cur_arg++;
4562 }
4563 else if (strcmp(args[cur_arg], "status-code") == 0) {
4564 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004565
Christopher Faulet61cc8522020-04-20 14:54:42 +02004566 if (in_pattern) {
4567 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4568 goto error;
4569 }
4570 if (!*(args[cur_arg+1])) {
4571 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4572 goto error;
4573 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004574
Christopher Faulet61cc8522020-04-20 14:54:42 +02004575 cur_arg++;
4576 release_sample_expr(status_expr);
4577 px->conf.args.ctx = ARGC_SRV;
4578 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4579 file, line, errmsg, &px->conf.args, NULL);
4580 if (!status_expr) {
4581 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4582 goto error;
4583 }
4584 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4585 memprintf(errmsg, "error detected while parsing status-code expression : "
4586 " fetch method '%s' extracts information from '%s', "
4587 "none of which is available here.\n",
4588 args[cur_arg], sample_src_names(status_expr->fetch->use));
4589 goto error;
4590 }
4591 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4592 }
4593 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4594 if (in_pattern) {
4595 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4596 goto error;
4597 }
4598 if (!*(args[cur_arg+1])) {
4599 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4600 goto error;
4601 }
4602 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4603 tout_st = HCHK_STATUS_L7TOUT;
4604 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4605 tout_st = HCHK_STATUS_L6TOUT;
4606 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4607 tout_st = HCHK_STATUS_L4TOUT;
4608 else {
4609 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4610 args[cur_arg], args[cur_arg+1]);
4611 goto error;
4612 }
4613 cur_arg++;
4614 }
4615 else {
4616 if (proto == TCPCHK_RULES_HTTP_CHK) {
4617 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004618 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004619 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004620 }
4621 else {
4622 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004623 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4624 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004625 }
4626 goto error;
4627 }
Christopher Faulet39708192020-05-05 10:47:36 +02004628 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004629 cur_arg++;
4630 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004631
Christopher Faulet61cc8522020-04-20 14:54:42 +02004632 chk = calloc(1, sizeof(*chk));
4633 if (!chk) {
4634 memprintf(errmsg, "out of memory");
4635 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004636 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004637 chk->action = TCPCHK_ACT_EXPECT;
4638 LIST_INIT(&chk->expect.onerror_fmt);
4639 LIST_INIT(&chk->expect.onsuccess_fmt);
4640 chk->comment = comment; comment = NULL;
4641 chk->expect.type = type;
4642 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004643 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004644 chk->expect.ok_status = ok_st;
4645 chk->expect.err_status = err_st;
4646 chk->expect.tout_status = tout_st;
4647 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004648
Christopher Faulet61cc8522020-04-20 14:54:42 +02004649 if (on_success_msg) {
4650 px->conf.args.ctx = ARGC_SRV;
4651 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4652 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4653 goto error;
4654 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004655 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004656 if (on_error_msg) {
4657 px->conf.args.ctx = ARGC_SRV;
4658 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4659 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4660 goto error;
4661 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004662 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004663
Christopher Faulet61cc8522020-04-20 14:54:42 +02004664 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004665 case TCPCHK_EXPECT_HTTP_STATUS: {
4666 const char *p = pattern;
4667 unsigned int c1,c2;
4668
4669 chk->expect.codes.codes = NULL;
4670 chk->expect.codes.num = 0;
4671 while (1) {
4672 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4673 if (*p == '-') {
4674 p++;
4675 c2 = read_uint(&p, pattern + strlen(pattern));
4676 }
4677 if (c1 > c2) {
4678 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4679 goto error;
4680 }
4681
4682 chk->expect.codes.num++;
4683 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4684 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4685 if (!chk->expect.codes.codes) {
4686 memprintf(errmsg, "out of memory");
4687 goto error;
4688 }
4689 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4690 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4691
4692 if (*p == '\0')
4693 break;
4694 if (*p != ',') {
4695 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4696 goto error;
4697 }
4698 p++;
4699 }
4700 break;
4701 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004702 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004703 case TCPCHK_EXPECT_HTTP_BODY:
4704 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004705 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004706 memprintf(errmsg, "out of memory");
4707 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004708 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004709 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004710 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004711 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004712
4713 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004714 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4715 goto error;
4716 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004717 chk->expect.data.len = len;
4718 break;
4719 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004720 case TCPCHK_EXPECT_STRING_REGEX:
4721 case TCPCHK_EXPECT_BINARY_REGEX:
4722 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4723 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004724 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004725 if (!chk->expect.regex)
4726 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004727 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004728
4729 case TCPCHK_EXPECT_STRING_LF:
4730 case TCPCHK_EXPECT_BINARY_LF:
4731 case TCPCHK_EXPECT_HTTP_BODY_LF:
4732 LIST_INIT(&chk->expect.fmt);
4733 px->conf.args.ctx = ARGC_SRV;
4734 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4735 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4736 goto error;
4737 }
4738 break;
4739
Christopher Faulet39708192020-05-05 10:47:36 +02004740 case TCPCHK_EXPECT_HTTP_HEADER:
4741 if (!npat) {
4742 memprintf(errmsg, "unexpected error, undefined header name pattern");
4743 goto error;
4744 }
4745 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4746 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4747 if (!chk->expect.hdr.name_re)
4748 goto error;
4749 }
4750 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4751 px->conf.args.ctx = ARGC_SRV;
4752 LIST_INIT(&chk->expect.hdr.name_fmt);
4753 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4754 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4755 goto error;
4756 }
4757 }
4758 else {
4759 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4760 if (!isttest(chk->expect.hdr.name)) {
4761 memprintf(errmsg, "out of memory");
4762 goto error;
4763 }
4764 }
4765
4766 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4767 chk->expect.hdr.value = IST_NULL;
4768 break;
4769 }
4770
4771 if (!vpat) {
4772 memprintf(errmsg, "unexpected error, undefined header value pattern");
4773 goto error;
4774 }
4775 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4776 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4777 if (!chk->expect.hdr.value_re)
4778 goto error;
4779 }
4780 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4781 px->conf.args.ctx = ARGC_SRV;
4782 LIST_INIT(&chk->expect.hdr.value_fmt);
4783 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4784 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4785 goto error;
4786 }
4787 }
4788 else {
4789 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4790 if (!isttest(chk->expect.hdr.value)) {
4791 memprintf(errmsg, "out of memory");
4792 goto error;
4793 }
4794 }
4795
Christopher Faulet61cc8522020-04-20 14:54:42 +02004796 break;
4797 case TCPCHK_EXPECT_CUSTOM:
4798 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4799 break;
4800 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004801 memprintf(errmsg, "pattern not found");
4802 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004803 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004804
Christopher Faulet61cc8522020-04-20 14:54:42 +02004805 /* All tcp-check expect points back to the first inverse expect rule in
4806 * a chain of one or more expect rule, potentially itself.
4807 */
4808 chk->expect.head = chk;
4809 list_for_each_entry_rev(prev_check, rules, list) {
4810 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4811 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4812 chk->expect.head = prev_check;
4813 continue;
4814 }
4815 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4816 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004817 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004818 return chk;
4819
4820 error:
4821 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004822 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004823 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004824 return NULL;
4825}
4826
Christopher Faulet61cc8522020-04-20 14:54:42 +02004827/* Overwrites fields of the old http send rule with those of the new one. When
4828 * replaced, old values are freed and replaced by the new ones. New values are
4829 * not copied but transferred. At the end <new> should be empty and can be
4830 * safely released. This function never fails.
4831 */
4832static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004833{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004834 struct logformat_node *lf, *lfb;
4835 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004836
Christopher Faulet404f9192020-04-09 23:13:54 +02004837
Christopher Faulet61cc8522020-04-20 14:54:42 +02004838 if (new->send.http.meth.str.area) {
4839 free(old->send.http.meth.str.area);
4840 old->send.http.meth.meth = new->send.http.meth.meth;
4841 old->send.http.meth.str.area = new->send.http.meth.str.area;
4842 old->send.http.meth.str.data = new->send.http.meth.str.data;
4843 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004844 }
4845
Christopher Faulet61cc8522020-04-20 14:54:42 +02004846 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4847 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004848 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004849 else
4850 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4851 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4852 old->send.http.uri = new->send.http.uri;
4853 new->send.http.uri = IST_NULL;
4854 }
4855 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4856 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004857 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004858 else
4859 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4860 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4861 LIST_INIT(&old->send.http.uri_fmt);
4862 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4863 LIST_DEL(&lf->list);
4864 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4865 }
4866 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004867
Christopher Faulet61cc8522020-04-20 14:54:42 +02004868 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004869 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004870 old->send.http.vsn = new->send.http.vsn;
4871 new->send.http.vsn = IST_NULL;
4872 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004873
Christopher Faulet61cc8522020-04-20 14:54:42 +02004874 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4875 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4876 LIST_DEL(&hdr->list);
4877 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004878 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004879
4880 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4881 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004882 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004883 else
4884 free_tcpcheck_fmt(&old->send.http.body_fmt);
4885 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4886 old->send.http.body = new->send.http.body;
4887 new->send.http.body = IST_NULL;
4888 }
4889 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4890 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004891 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004892 else
4893 free_tcpcheck_fmt(&old->send.http.body_fmt);
4894 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4895 LIST_INIT(&old->send.http.body_fmt);
4896 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4897 LIST_DEL(&lf->list);
4898 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4899 }
4900 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004901}
4902
Christopher Faulet61cc8522020-04-20 14:54:42 +02004903/* Internal function used to add an http-check rule in a list during the config
4904 * parsing step. Depending on its type, and the previously inserted rules, a
4905 * specific action may be performed or an error may be reported. This functions
4906 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4907 * message.
4908 */
4909static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004910{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004911 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004912
Christopher Faulet61cc8522020-04-20 14:54:42 +02004913 /* the implicit send rule coming from an "option httpchk" line must be
4914 * merged with the first explici http-check send rule, if
4915 * any. Depdending the declaration order some tests are required.
4916 *
4917 * Some tests is also required for other kinds of http-check rules to be
4918 * sure the ruleset remains valid.
4919 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004920
Christopher Faulet61cc8522020-04-20 14:54:42 +02004921 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4922 /* Tries to add an implcit http-check send rule from an "option httpchk" line.
4923 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4924 * following tests are performed :
4925 *
4926 * 1- If there is no such rule or if it is not a send rule, the implicit send
4927 * rule is pushed in front of the ruleset
4928 *
4929 * 2- If it is another implicit send rule, it is replaced with the new one.
4930 *
4931 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
4932 * both, overwritting the old send rule (the explicit one) with info of the
4933 * new send rule (the implicit one).
4934 */
4935 r = get_first_tcpcheck_rule(rules);
4936 if (r && r->action == TCPCHK_ACT_CONNECT)
4937 r = get_next_tcpcheck_rule(rules, r);
4938 if (!r || r->action != TCPCHK_ACT_SEND)
4939 LIST_ADD(rules->list, &chk->list);
4940 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4941 LIST_DEL(&r->list);
4942 free_tcpcheck(r, 0);
4943 LIST_ADD(rules->list, &chk->list);
4944 }
4945 else {
4946 tcpcheck_overwrite_send_http_rule(r, chk);
4947 free_tcpcheck(chk, 0);
4948 }
4949 }
4950 else {
4951 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4952 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4953 * with an existing implicit send rule, if any. At the end, if there is no error,
4954 * the rule is appended to the list.
4955 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004956
Christopher Faulet61cc8522020-04-20 14:54:42 +02004957 r = get_last_tcpcheck_rule(rules);
4958 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4959 /* no error */;
4960 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4961 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4962 chk->index+1);
4963 return 0;
4964 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004965 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004966 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4967 chk->index+1);
4968 return 0;
4969 }
4970 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4971 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4972 chk->index+1);
4973 return 0;
4974 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004975
Christopher Faulet61cc8522020-04-20 14:54:42 +02004976 if (chk->action == TCPCHK_ACT_SEND) {
4977 r = get_first_tcpcheck_rule(rules);
4978 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4979 tcpcheck_overwrite_send_http_rule(r, chk);
4980 free_tcpcheck(chk, 0);
4981 LIST_DEL(&r->list);
4982 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4983 chk = r;
4984 }
4985 }
4986 LIST_ADDQ(rules->list, &chk->list);
4987 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004988 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004989}
4990
Christopher Faulet61cc8522020-04-20 14:54:42 +02004991/**************************************************************************/
4992/************************** Init/deinit checks ****************************/
4993/**************************************************************************/
4994static const char *init_check(struct check *check, int type)
4995{
4996 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004997
Christopher Faulet61cc8522020-04-20 14:54:42 +02004998 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4999 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02005000
Christopher Faulet61cc8522020-04-20 14:54:42 +02005001 check->bi.area = calloc(check->bi.size, sizeof(char));
5002 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02005003
Christopher Faulet61cc8522020-04-20 14:54:42 +02005004 if (!check->bi.area || !check->bo.area)
5005 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005006
Christopher Faulet61cc8522020-04-20 14:54:42 +02005007 check->wait_list.tasklet = tasklet_new();
5008 if (!check->wait_list.tasklet)
5009 return "out of memory while allocating check tasklet";
5010 check->wait_list.events = 0;
5011 check->wait_list.tasklet->process = event_srv_chk_io;
5012 check->wait_list.tasklet->context = check;
5013 return NULL;
5014}
5015
5016void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005017{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005018 task_destroy(check->task);
5019 if (check->wait_list.tasklet)
5020 tasklet_free(check->wait_list.tasklet);
5021
5022 free(check->bi.area);
5023 free(check->bo.area);
5024 if (check->cs) {
5025 free(check->cs->conn);
5026 check->cs->conn = NULL;
5027 cs_free(check->cs);
5028 check->cs = NULL;
5029 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005030}
5031
Christopher Faulet61cc8522020-04-20 14:54:42 +02005032/* manages a server health-check. Returns the time the task accepts to wait, or
5033 * TIME_ETERNITY for infinity.
5034 */
5035static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005036{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005037 struct check *check = context;
5038
5039 if (check->type == PR_O2_EXT_CHK)
5040 return process_chk_proc(t, context, state);
5041 return process_chk_conn(t, context, state);
5042
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005043}
5044
Christopher Faulet61cc8522020-04-20 14:54:42 +02005045
5046static int start_check_task(struct check *check, int mininter,
5047 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005048{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005049 struct task *t;
5050 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005051
Christopher Faulet61cc8522020-04-20 14:54:42 +02005052 if (check->type == PR_O2_EXT_CHK)
5053 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005054
Christopher Faulet61cc8522020-04-20 14:54:42 +02005055 /* task for the check */
5056 if ((t = task_new(thread_mask)) == NULL) {
5057 ha_alert("Starting [%s:%s] check: out of memory.\n",
5058 check->server->proxy->id, check->server->id);
5059 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005060 }
5061
Christopher Faulet61cc8522020-04-20 14:54:42 +02005062 check->task = t;
5063 t->process = process_chk;
5064 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005065
Christopher Faulet61cc8522020-04-20 14:54:42 +02005066 if (mininter < srv_getinter(check))
5067 mininter = srv_getinter(check);
5068
5069 if (global.max_spread_checks && mininter > global.max_spread_checks)
5070 mininter = global.max_spread_checks;
5071
5072 /* check this every ms */
5073 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5074 check->start = now;
5075 task_queue(t);
5076
5077 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005078}
5079
Christopher Faulet61cc8522020-04-20 14:54:42 +02005080/* updates the server's weight during a warmup stage. Once the final weight is
5081 * reached, the task automatically stops. Note that any server status change
5082 * must have updated s->last_change accordingly.
5083 */
5084static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005085{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005086 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005087
Christopher Faulet61cc8522020-04-20 14:54:42 +02005088 /* by default, plan on stopping the task */
5089 t->expire = TICK_ETERNITY;
5090 if ((s->next_admin & SRV_ADMF_MAINT) ||
5091 (s->next_state != SRV_ST_STARTING))
5092 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005093
Christopher Faulet61cc8522020-04-20 14:54:42 +02005094 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005095
Christopher Faulet61cc8522020-04-20 14:54:42 +02005096 /* recalculate the weights and update the state */
5097 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005098
Christopher Faulet61cc8522020-04-20 14:54:42 +02005099 /* probably that we can refill this server with a bit more connections */
5100 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005101
Christopher Faulet61cc8522020-04-20 14:54:42 +02005102 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005103
Christopher Faulet61cc8522020-04-20 14:54:42 +02005104 /* get back there in 1 second or 1/20th of the slowstart interval,
5105 * whichever is greater, resulting in small 5% steps.
5106 */
5107 if (s->next_state == SRV_ST_STARTING)
5108 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5109 return t;
5110}
5111
5112/*
5113 * Start health-check.
5114 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5115 */
5116static int start_checks()
5117{
5118
5119 struct proxy *px;
5120 struct server *s;
5121 struct task *t;
5122 int nbcheck=0, mininter=0, srvpos=0;
5123
5124 /* 0- init the dummy frontend used to create all checks sessions */
5125 init_new_proxy(&checks_fe);
5126 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5127 checks_fe.mode = PR_MODE_TCP;
5128 checks_fe.maxconn = 0;
5129 checks_fe.conn_retries = CONN_RETRIES;
5130 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5131 checks_fe.timeout.client = TICK_ETERNITY;
5132
5133 /* 1- count the checkers to run simultaneously.
5134 * We also determine the minimum interval among all of those which
5135 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5136 * will be used to spread their start-up date. Those which have
5137 * a shorter interval will start independently and will not dictate
5138 * too short an interval for all others.
5139 */
5140 for (px = proxies_list; px; px = px->next) {
5141 for (s = px->srv; s; s = s->next) {
5142 if (s->slowstart) {
5143 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5144 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5145 return ERR_ALERT | ERR_FATAL;
5146 }
5147 /* We need a warmup task that will be called when the server
5148 * state switches from down to up.
5149 */
5150 s->warmup = t;
5151 t->process = server_warmup;
5152 t->context = s;
5153 /* server can be in this state only because of */
5154 if (s->next_state == SRV_ST_STARTING)
5155 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 +02005156 }
5157
Christopher Faulet61cc8522020-04-20 14:54:42 +02005158 if (s->check.state & CHK_ST_CONFIGURED) {
5159 nbcheck++;
5160 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5161 (!mininter || mininter > srv_getinter(&s->check)))
5162 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005163 }
5164
Christopher Faulet61cc8522020-04-20 14:54:42 +02005165 if (s->agent.state & CHK_ST_CONFIGURED) {
5166 nbcheck++;
5167 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5168 (!mininter || mininter > srv_getinter(&s->agent)))
5169 mininter = srv_getinter(&s->agent);
5170 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005171 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005172 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005173
Christopher Faulet61cc8522020-04-20 14:54:42 +02005174 if (!nbcheck)
5175 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005176
Christopher Faulet61cc8522020-04-20 14:54:42 +02005177 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005178
Christopher Faulet61cc8522020-04-20 14:54:42 +02005179 /*
5180 * 2- start them as far as possible from each others. For this, we will
5181 * start them after their interval set to the min interval divided by
5182 * the number of servers, weighted by the server's position in the list.
5183 */
5184 for (px = proxies_list; px; px = px->next) {
5185 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5186 if (init_pid_list()) {
5187 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5188 return ERR_ALERT | ERR_FATAL;
5189 }
5190 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005191
Christopher Faulet61cc8522020-04-20 14:54:42 +02005192 for (s = px->srv; s; s = s->next) {
5193 /* A task for the main check */
5194 if (s->check.state & CHK_ST_CONFIGURED) {
5195 if (s->check.type == PR_O2_EXT_CHK) {
5196 if (!prepare_external_check(&s->check))
5197 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005198 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005199 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5200 return ERR_ALERT | ERR_FATAL;
5201 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005202 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005203
Christopher Faulet61cc8522020-04-20 14:54:42 +02005204 /* A task for a auxiliary agent check */
5205 if (s->agent.state & CHK_ST_CONFIGURED) {
5206 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5207 return ERR_ALERT | ERR_FATAL;
5208 }
5209 srvpos++;
5210 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005211 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005212 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005213 return 0;
5214}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005215
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005216
Christopher Faulet61cc8522020-04-20 14:54:42 +02005217/*
5218 * Return value:
5219 * the port to be used for the health check
5220 * 0 in case no port could be found for the check
5221 */
5222static int srv_check_healthcheck_port(struct check *chk)
5223{
5224 int i = 0;
5225 struct server *srv = NULL;
5226
5227 srv = chk->server;
5228
5229 /* by default, we use the health check port ocnfigured */
5230 if (chk->port > 0)
5231 return chk->port;
5232
5233 /* try to get the port from check_core.addr if check.port not set */
5234 i = get_host_port(&chk->addr);
5235 if (i > 0)
5236 return i;
5237
5238 /* try to get the port from server address */
5239 /* prevent MAPPORTS from working at this point, since checks could
5240 * not be performed in such case (MAPPORTS impose a relative ports
5241 * based on live traffic)
5242 */
5243 if (srv->flags & SRV_F_MAPPORTS)
5244 return 0;
5245
5246 i = srv->svc_port; /* by default */
5247 if (i > 0)
5248 return i;
5249
5250 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005251}
5252
Christopher Faulet61cc8522020-04-20 14:54:42 +02005253/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5254 * if an error occurred.
5255 */
5256static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005257{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005258 const char *err;
5259 struct tcpcheck_rule *r;
5260 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005261
Christopher Faulet61cc8522020-04-20 14:54:42 +02005262 if (!srv->do_check)
5263 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005264
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005265
Christopher Faulet61cc8522020-04-20 14:54:42 +02005266 /* If neither a port nor an addr was specified and no check transport
5267 * layer is forced, then the transport layer used by the checks is the
5268 * same as for the production traffic. Otherwise we use raw_sock by
5269 * default, unless one is specified.
5270 */
5271 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5272 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5273 srv->check.use_ssl = srv->use_ssl;
5274 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005275 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005276 else if (srv->check.use_ssl == 1)
5277 srv->check.xprt = xprt_get(XPRT_SSL);
5278 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005279 }
5280
Christopher Faulet12882cf2020-04-23 15:50:18 +02005281 /* Inherit the mux protocol from the server if not already defined for
5282 * the check
5283 */
5284 if (srv->mux_proto && !srv->check.mux_proto)
5285 srv->check.mux_proto = srv->mux_proto;
5286
Christopher Faulet61cc8522020-04-20 14:54:42 +02005287 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005288
Christopher Faulet61cc8522020-04-20 14:54:42 +02005289 /* We need at least a service port, a check port or the first tcp-check
5290 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5291 */
5292 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5293 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5294 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005295
Christopher Faulet61cc8522020-04-20 14:54:42 +02005296 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5297 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5298 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5299 ret |= ERR_ALERT | ERR_ABORT;
5300 goto out;
5301 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005302
Christopher Faulet61cc8522020-04-20 14:54:42 +02005303 /* search the first action (connect / send / expect) in the list */
5304 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5305 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5306 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5307 "nor tcp_check rule 'connect' with port information.\n",
5308 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5309 ret |= ERR_ALERT | ERR_ABORT;
5310 goto out;
5311 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005312
Christopher Faulet61cc8522020-04-20 14:54:42 +02005313 /* scan the tcp-check ruleset to ensure a port has been configured */
5314 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5315 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5316 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5317 "and a tcp_check rule 'connect' with no port information.\n",
5318 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5319 ret |= ERR_ALERT | ERR_ABORT;
5320 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005321 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005322 }
5323
Christopher Faulet61cc8522020-04-20 14:54:42 +02005324 init:
5325 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5326 struct tcpcheck_ruleset *rs = NULL;
5327 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5328 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005329
Christopher Faulet61cc8522020-04-20 14:54:42 +02005330 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5331 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005332
Christopher Faulet61cc8522020-04-20 14:54:42 +02005333 rs = find_tcpcheck_ruleset("*tcp-check");
5334 if (!rs) {
5335 rs = create_tcpcheck_ruleset("*tcp-check");
5336 if (rs == NULL) {
5337 ha_alert("config: %s '%s': out of memory.\n",
5338 proxy_type_str(srv->proxy), srv->proxy->id);
5339 ret |= ERR_ALERT | ERR_FATAL;
5340 goto out;
5341 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005342 }
5343
Christopher Faulet61cc8522020-04-20 14:54:42 +02005344 free_tcpcheck_vars(&rules->preset_vars);
5345 rules->list = &rs->rules;
5346 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005347 }
5348
Christopher Faulet61cc8522020-04-20 14:54:42 +02005349 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5350 if (err) {
5351 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5352 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5353 ret |= ERR_ALERT | ERR_ABORT;
5354 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005355 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005356 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5357 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005358
Christopher Faulet61cc8522020-04-20 14:54:42 +02005359 out:
5360 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005361}
5362
Christopher Faulet61cc8522020-04-20 14:54:42 +02005363/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5364 * if an error occurred.
5365 */
5366static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005367{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005368 struct tcpcheck_rule *chk;
5369 const char *err;
5370 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005371
Christopher Faulet61cc8522020-04-20 14:54:42 +02005372 if (!srv->do_agent)
5373 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005374
Christopher Faulet61cc8522020-04-20 14:54:42 +02005375 /* If there is no connect rule preceeding all send / expect rules, an
5376 * implicit one is inserted before all others.
5377 */
5378 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5379 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5380 chk = calloc(1, sizeof(*chk));
5381 if (!chk) {
5382 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5383 " to agent-check for server '%s' (out of memory).\n",
5384 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5385 ret |= ERR_ALERT | ERR_FATAL;
5386 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005387 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005388 chk->action = TCPCHK_ACT_CONNECT;
5389 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5390 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005391 }
5392
Christopher Faulete5870d82020-04-15 11:32:03 +02005393
Christopher Faulet61cc8522020-04-20 14:54:42 +02005394 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5395 if (err) {
5396 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5397 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5398 ret |= ERR_ALERT | ERR_ABORT;
5399 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005400 }
5401
Christopher Faulet61cc8522020-04-20 14:54:42 +02005402 if (!srv->agent.inter)
5403 srv->agent.inter = srv->check.inter;
5404
5405 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5406 global.maxsock++;
5407
5408 out:
5409 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005410}
5411
Christopher Faulet61cc8522020-04-20 14:54:42 +02005412/* Check tcp-check health-check configuration for the proxy <px>. */
5413static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005414{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005415 struct tcpcheck_rule *chk, *back;
5416 char *comment = NULL, *errmsg = NULL;
5417 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5418 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005419
Christopher Faulet61cc8522020-04-20 14:54:42 +02005420 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5421 deinit_proxy_tcpcheck(px);
5422 goto out;
5423 }
5424
5425 free(px->check_command);
5426 free(px->check_path);
5427 px->check_command = px->check_path = NULL;
5428
5429 if (!px->tcpcheck_rules.list) {
5430 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5431 ret |= ERR_ALERT | ERR_FATAL;
5432 goto out;
5433 }
5434
5435 /* HTTP ruleset only : */
5436 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5437 struct tcpcheck_rule *next;
5438
5439 /* move remaining implicit send rule from "option httpchk" line to the right place.
5440 * If such rule exists, it must be the first one. In this case, the rule is moved
5441 * after the first connect rule, if any. Otherwise, nothing is done.
5442 */
5443 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5444 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5445 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5446 if (next && next->action == TCPCHK_ACT_CONNECT) {
5447 LIST_DEL(&chk->list);
5448 LIST_ADD(&next->list, &chk->list);
5449 chk->index = next->index;
5450 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005451 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005452
5453 /* add implicit expect rule if the last one is a send. It is inherited from previous
5454 * versions where the http expect rule was optional. Now it is possible to chained
5455 * send/expect rules but the last expect may still be implicit.
5456 */
5457 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5458 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005459 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005460 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5461 px->conf.file, px->conf.line, &errmsg);
5462 if (!next) {
5463 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5464 "(%s).\n", px->id, errmsg);
5465 free(errmsg);
5466 ret |= ERR_ALERT | ERR_FATAL;
5467 goto out;
5468 }
5469 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5470 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005471 }
5472 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005473
5474 /* For all ruleset: */
5475
5476 /* If there is no connect rule preceeding all send / expect rules, an
5477 * implicit one is inserted before all others.
5478 */
5479 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5480 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5481 chk = calloc(1, sizeof(*chk));
5482 if (!chk) {
5483 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5484 "(out of memory).\n", px->id);
5485 ret |= ERR_ALERT | ERR_FATAL;
5486 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005487 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005488 chk->action = TCPCHK_ACT_CONNECT;
5489 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5490 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5491 }
5492
5493 /* Remove all comment rules. To do so, when a such rule is found, the
5494 * comment is assigned to the following rule(s).
5495 */
5496 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5497 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5498 free(comment);
5499 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005500 }
5501
Christopher Faulet61cc8522020-04-20 14:54:42 +02005502 prev_action = chk->action;
5503 switch (chk->action) {
5504 case TCPCHK_ACT_COMMENT:
5505 free(comment);
5506 comment = chk->comment;
5507 LIST_DEL(&chk->list);
5508 free(chk);
5509 break;
5510 case TCPCHK_ACT_CONNECT:
5511 if (!chk->comment && comment)
5512 chk->comment = strdup(comment);
5513 /* fall though */
5514 case TCPCHK_ACT_ACTION_KW:
5515 free(comment);
5516 comment = NULL;
5517 break;
5518 case TCPCHK_ACT_SEND:
5519 case TCPCHK_ACT_EXPECT:
5520 if (!chk->comment && comment)
5521 chk->comment = strdup(comment);
5522 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005523 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005524 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005525 free(comment);
5526 comment = NULL;
5527
5528 out:
5529 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005530}
5531
Christopher Faulet61cc8522020-04-20 14:54:42 +02005532void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005533{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005534 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5535 px->tcpcheck_rules.flags = 0;
5536 px->tcpcheck_rules.list = NULL;
5537}
Christopher Faulete5870d82020-04-15 11:32:03 +02005538
Christopher Faulet61cc8522020-04-20 14:54:42 +02005539static void deinit_srv_check(struct server *srv)
5540{
5541 if (srv->check.state & CHK_ST_CONFIGURED)
5542 free_check(&srv->check);
5543 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5544 srv->do_check = 0;
5545}
Christopher Faulete5870d82020-04-15 11:32:03 +02005546
Christopher Faulet61cc8522020-04-20 14:54:42 +02005547
5548static void deinit_srv_agent_check(struct server *srv)
5549{
5550 if (srv->agent.tcpcheck_rules) {
5551 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5552 free(srv->agent.tcpcheck_rules);
5553 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005554 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005555
Christopher Faulet61cc8522020-04-20 14:54:42 +02005556 if (srv->agent.state & CHK_ST_CONFIGURED)
5557 free_check(&srv->agent);
5558
5559 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5560 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005561}
5562
Christopher Faulet61cc8522020-04-20 14:54:42 +02005563static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005564{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005565 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005566 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005567 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005568
Christopher Fauletd7cee712020-04-21 13:45:00 +02005569 node = ebpt_first(&shared_tcpchecks);
5570 while (node) {
5571 next = ebpt_next(node);
5572 ebpt_delete(node);
5573 free(node->key);
5574 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005575 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5576 LIST_DEL(&r->list);
5577 free_tcpcheck(r, 0);
5578 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005579 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005580 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005581 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005582}
Christopher Faulete5870d82020-04-15 11:32:03 +02005583
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005584
Christopher Faulet61cc8522020-04-20 14:54:42 +02005585REGISTER_POST_SERVER_CHECK(init_srv_check);
5586REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5587REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5588REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005589
Christopher Faulet61cc8522020-04-20 14:54:42 +02005590REGISTER_SERVER_DEINIT(deinit_srv_check);
5591REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5592REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5593REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005594
Christopher Faulet61cc8522020-04-20 14:54:42 +02005595/**************************************************************************/
5596/****************************** Email alerts ******************************/
5597/* NOTE: It may be pertinent to use an applet to handle email alerts */
5598/* instead of a tcp-check ruleset */
5599/**************************************************************************/
5600void email_alert_free(struct email_alert *alert)
5601{
5602 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005603
Christopher Faulet61cc8522020-04-20 14:54:42 +02005604 if (!alert)
5605 return;
5606
5607 if (alert->rules.list) {
5608 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5609 LIST_DEL(&rule->list);
5610 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005611 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005612 free_tcpcheck_vars(&alert->rules.preset_vars);
5613 free(alert->rules.list);
5614 alert->rules.list = NULL;
5615 }
5616 pool_free(pool_head_email_alert, alert);
5617}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005618
Christopher Faulet61cc8522020-04-20 14:54:42 +02005619static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5620{
5621 struct check *check = context;
5622 struct email_alertq *q;
5623 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005624
Christopher Faulet61cc8522020-04-20 14:54:42 +02005625 q = container_of(check, typeof(*q), check);
5626
5627 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5628 while (1) {
5629 if (!(check->state & CHK_ST_ENABLED)) {
5630 if (LIST_ISEMPTY(&q->email_alerts)) {
5631 /* All alerts processed, queue the task */
5632 t->expire = TICK_ETERNITY;
5633 task_queue(t);
5634 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005635 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005636
5637 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5638 LIST_DEL(&alert->list);
5639 t->expire = now_ms;
5640 check->tcpcheck_rules = &alert->rules;
5641 check->status = HCHK_STATUS_INI;
5642 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005643 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005644
5645 process_chk(t, context, state);
5646 if (check->state & CHK_ST_INPROGRESS)
5647 break;
5648
5649 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5650 email_alert_free(alert);
5651 check->tcpcheck_rules = NULL;
5652 check->server = NULL;
5653 check->state &= ~CHK_ST_ENABLED;
5654 }
5655 end:
5656 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5657 return t;
5658}
5659
5660/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5661 *
5662 * The function returns 1 in success case, otherwise, it returns 0 and err is
5663 * filled.
5664 */
5665int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5666{
5667 struct mailer *mailer;
5668 struct email_alertq *queues;
5669 const char *err_str;
5670 int i = 0;
5671
5672 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5673 memprintf(err, "out of memory while allocating mailer alerts queues");
5674 goto fail_no_queue;
5675 }
5676
5677 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5678 struct email_alertq *q = &queues[i];
5679 struct check *check = &q->check;
5680 struct task *t;
5681
5682 LIST_INIT(&q->email_alerts);
5683 HA_SPIN_INIT(&q->lock);
5684 check->inter = mls->timeout.mail;
5685 check->rise = DEF_AGENT_RISETIME;
5686 check->proxy = p;
5687 check->fall = DEF_AGENT_FALLTIME;
5688 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5689 memprintf(err, "%s", err_str);
5690 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005691 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005692
5693 check->xprt = mailer->xprt;
5694 check->addr = mailer->addr;
5695 check->port = get_host_port(&mailer->addr);
5696
5697 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5698 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005699 goto error;
5700 }
5701
Christopher Faulet61cc8522020-04-20 14:54:42 +02005702 check->task = t;
5703 t->process = process_email_alert;
5704 t->context = check;
5705
5706 /* check this in one ms */
5707 t->expire = TICK_ETERNITY;
5708 check->start = now;
5709 task_queue(t);
5710 }
5711
5712 mls->users++;
5713 free(p->email_alert.mailers.name);
5714 p->email_alert.mailers.m = mls;
5715 p->email_alert.queues = queues;
5716 return 0;
5717
5718 error:
5719 for (i = 0; i < mls->count; i++) {
5720 struct email_alertq *q = &queues[i];
5721 struct check *check = &q->check;
5722
5723 free_check(check);
5724 }
5725 free(queues);
5726 fail_no_queue:
5727 return 1;
5728}
5729
5730static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5731{
5732 struct tcpcheck_rule *tcpcheck, *prev_check;
5733 struct tcpcheck_expect *expect;
5734
5735 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5736 return 0;
5737 memset(tcpcheck, 0, sizeof(*tcpcheck));
5738 tcpcheck->action = TCPCHK_ACT_EXPECT;
5739
5740 expect = &tcpcheck->expect;
5741 expect->type = TCPCHK_EXPECT_STRING;
5742 LIST_INIT(&expect->onerror_fmt);
5743 LIST_INIT(&expect->onsuccess_fmt);
5744 expect->ok_status = HCHK_STATUS_L7OKD;
5745 expect->err_status = HCHK_STATUS_L7RSP;
5746 expect->tout_status = HCHK_STATUS_L7TOUT;
5747 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005748 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005749 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5750 return 0;
5751 }
5752
5753 /* All tcp-check expect points back to the first inverse expect rule
5754 * in a chain of one or more expect rule, potentially itself.
5755 */
5756 tcpcheck->expect.head = tcpcheck;
5757 list_for_each_entry_rev(prev_check, rules->list, list) {
5758 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5759 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5760 tcpcheck->expect.head = prev_check;
5761 continue;
5762 }
5763 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5764 break;
5765 }
5766 LIST_ADDQ(rules->list, &tcpcheck->list);
5767 return 1;
5768}
5769
5770static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5771{
5772 struct tcpcheck_rule *tcpcheck;
5773 struct tcpcheck_send *send;
5774 const char *in;
5775 char *dst;
5776 int i;
5777
5778 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5779 return 0;
5780 memset(tcpcheck, 0, sizeof(*tcpcheck));
5781 tcpcheck->action = TCPCHK_ACT_SEND;
5782
5783 send = &tcpcheck->send;
5784 send->type = TCPCHK_SEND_STRING;
5785
5786 for (i = 0; strs[i]; i++)
5787 send->data.len += strlen(strs[i]);
5788
Christopher Fauletb61caf42020-04-21 10:57:42 +02005789 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005790 if (!isttest(send->data)) {
5791 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5792 return 0;
5793 }
5794
Christopher Fauletb61caf42020-04-21 10:57:42 +02005795 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005796 for (i = 0; strs[i]; i++)
5797 for (in = strs[i]; (*dst = *in++); dst++);
5798 *dst = 0;
5799
5800 LIST_ADDQ(rules->list, &tcpcheck->list);
5801 return 1;
5802}
5803
5804static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5805 struct email_alertq *q, const char *msg)
5806{
5807 struct email_alert *alert;
5808 struct tcpcheck_rule *tcpcheck;
5809 struct check *check = &q->check;
5810
5811 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5812 goto error;
5813 LIST_INIT(&alert->list);
5814 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5815 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5816 if (!alert->rules.list)
5817 goto error;
5818 LIST_INIT(alert->rules.list);
5819 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5820 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005821
Christopher Faulet61cc8522020-04-20 14:54:42 +02005822 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5823 goto error;
5824 memset(tcpcheck, 0, sizeof(*tcpcheck));
5825 tcpcheck->action = TCPCHK_ACT_CONNECT;
5826 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005827
Christopher Faulet61cc8522020-04-20 14:54:42 +02005828 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005829
Christopher Faulet61cc8522020-04-20 14:54:42 +02005830 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005831 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005832
5833 {
5834 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5835 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5836 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005837 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005838
Christopher Faulet61cc8522020-04-20 14:54:42 +02005839 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5840 goto error;
5841
5842 {
5843 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\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 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005847
5848 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5849 goto error;
5850
5851 {
5852 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5853 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005854 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005855 }
5856
Christopher Faulet61cc8522020-04-20 14:54:42 +02005857 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5858 goto error;
5859
5860 {
5861 const char * const strs[2] = { "DATA\r\n" };
5862 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005863 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005864 }
5865
5866 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5867 goto error;
5868
5869 {
5870 struct tm tm;
5871 char datestr[48];
5872 const char * const strs[18] = {
5873 "From: ", p->email_alert.from, "\r\n",
5874 "To: ", p->email_alert.to, "\r\n",
5875 "Date: ", datestr, "\r\n",
5876 "Subject: [HAproxy Alert] ", msg, "\r\n",
5877 "\r\n",
5878 msg, "\r\n",
5879 "\r\n",
5880 ".\r\n",
5881 NULL
5882 };
5883
5884 get_localtime(date.tv_sec, &tm);
5885
5886 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005887 goto error;
5888 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005889
5890 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005891 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005892 }
5893
5894 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005895 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005896
5897 {
5898 const char * const strs[2] = { "QUIT\r\n" };
5899 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5900 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005901 }
5902
Christopher Faulet61cc8522020-04-20 14:54:42 +02005903 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5904 goto error;
5905
5906 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5907 task_wakeup(check->task, TASK_WOKEN_MSG);
5908 LIST_ADDQ(&q->email_alerts, &alert->list);
5909 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5910 return 1;
5911
5912error:
5913 email_alert_free(alert);
5914 return 0;
5915}
5916
5917static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5918{
5919 int i;
5920 struct mailer *mailer;
5921
5922 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5923 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5924 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5925 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5926 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005927 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005928 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005929
Christopher Faulet61cc8522020-04-20 14:54:42 +02005930 return;
5931}
5932
5933/*
5934 * Send email alert if configured.
5935 */
5936void send_email_alert(struct server *s, int level, const char *format, ...)
5937{
5938 va_list argp;
5939 char buf[1024];
5940 int len;
5941 struct proxy *p = s->proxy;
5942
5943 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5944 return;
5945
5946 va_start(argp, format);
5947 len = vsnprintf(buf, sizeof(buf), format, argp);
5948 va_end(argp);
5949
5950 if (len < 0 || len >= sizeof(buf)) {
5951 ha_alert("Email alert [%s] could not format message\n", p->id);
5952 return;
5953 }
5954
5955 enqueue_email_alert(p, s, buf);
5956}
5957
5958/**************************************************************************/
5959/************************** Check sample fetches **************************/
5960/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005961
Christopher Faulet61cc8522020-04-20 14:54:42 +02005962static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005963 { /* END */ },
5964}};
5965
5966INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5967
5968
5969/**************************************************************************/
5970/************************ Check's parsing functions ***********************/
5971/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005972/* Parses the "tcp-check" proxy keyword */
5973static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5974 struct proxy *defpx, const char *file, int line,
5975 char **errmsg)
5976{
Christopher Faulet404f9192020-04-09 23:13:54 +02005977 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005978 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005979 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005980
5981 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5982 ret = 1;
5983
Christopher Faulet404f9192020-04-09 23:13:54 +02005984 /* Deduce the ruleset name from the proxy info */
5985 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5986 ((curpx == defpx) ? "defaults" : curpx->id),
5987 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005988
Christopher Faulet61cc8522020-04-20 14:54:42 +02005989 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005990 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005991 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005992 if (rs == NULL) {
5993 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005994 goto error;
5995 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005996 }
5997
Gaetan Rivet5301b012020-02-25 17:19:17 +01005998 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005999 if (!LIST_ISEMPTY(&rs->rules)) {
6000 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01006001 index = chk->index + 1;
6002 }
6003
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006004 cur_arg = 1;
6005 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006006 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006007 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
6008 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006009 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006010 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02006011 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006012 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02006013 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006014 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01006015 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
6016
6017 if (!kw) {
6018 action_kw_tcp_check_build_list(&trash);
6019 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
6020 "%s%s. but got '%s'",
6021 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6022 goto error;
6023 }
Christopher Faulet404f9192020-04-09 23:13:54 +02006024 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006025 }
6026
6027 if (!chk) {
6028 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6029 goto error;
6030 }
Christopher Faulet528f4812020-04-28 10:47:28 +02006031 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006032
6033 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01006034 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02006035 LIST_ADDQ(&rs->rules, &chk->list);
6036
6037 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02006038 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006039 /* Use this ruleset if the proxy already has tcp-check enabled */
6040 curpx->tcpcheck_rules.list = &rs->rules;
6041 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
6042 }
6043 else {
6044 /* mark this ruleset as unused for now */
6045 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
6046 }
6047
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006048 return ret;
6049
6050 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006051 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006052 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006053 return -1;
6054}
6055
Christopher Faulet51b129f2020-04-09 15:54:18 +02006056/* Parses the "http-check" proxy keyword */
6057static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6058 struct proxy *defpx, const char *file, int line,
6059 char **errmsg)
6060{
Christopher Faulete5870d82020-04-15 11:32:03 +02006061 struct tcpcheck_ruleset *rs = NULL;
6062 struct tcpcheck_rule *chk = NULL;
6063 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006064
6065 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6066 ret = 1;
6067
6068 cur_arg = 1;
6069 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6070 /* enable a graceful server shutdown on an HTTP 404 response */
6071 curpx->options |= PR_O_DISABLE404;
6072 if (too_many_args(1, args, errmsg, NULL))
6073 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006074 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006075 }
6076 else if (strcmp(args[cur_arg], "send-state") == 0) {
6077 /* enable emission of the apparent state of a server in HTTP checks */
6078 curpx->options2 |= PR_O2_CHK_SNDST;
6079 if (too_many_args(1, args, errmsg, NULL))
6080 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006081 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006082 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006083
Christopher Faulete5870d82020-04-15 11:32:03 +02006084 /* Deduce the ruleset name from the proxy info */
6085 chunk_printf(&trash, "*http-check-%s_%s-%d",
6086 ((curpx == defpx) ? "defaults" : curpx->id),
6087 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006088
Christopher Faulet61cc8522020-04-20 14:54:42 +02006089 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006090 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006091 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006092 if (rs == NULL) {
6093 memprintf(errmsg, "out of memory.\n");
6094 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006095 }
6096 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006097
Christopher Faulete5870d82020-04-15 11:32:03 +02006098 index = 0;
6099 if (!LIST_ISEMPTY(&rs->rules)) {
6100 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6101 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6102 index = chk->index + 1;
6103 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006104
Christopher Faulete5870d82020-04-15 11:32:03 +02006105 if (strcmp(args[cur_arg], "connect") == 0)
6106 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6107 else if (strcmp(args[cur_arg], "send") == 0)
6108 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6109 else if (strcmp(args[cur_arg], "expect") == 0)
6110 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6111 file, line, errmsg);
6112 else if (strcmp(args[cur_arg], "comment") == 0)
6113 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6114 else {
6115 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006116
Christopher Faulete5870d82020-04-15 11:32:03 +02006117 if (!kw) {
6118 action_kw_tcp_check_build_list(&trash);
6119 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6120 " 'send', 'expect'%s%s. but got '%s'",
6121 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6122 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006123 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006124 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6125 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006126
Christopher Faulete5870d82020-04-15 11:32:03 +02006127 if (!chk) {
6128 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6129 goto error;
6130 }
6131 ret = (*errmsg != NULL); /* Handle warning */
6132
6133 chk->index = index;
6134 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6135 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6136 /* Use this ruleset if the proxy already has http-check enabled */
6137 curpx->tcpcheck_rules.list = &rs->rules;
6138 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6139 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6140 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6141 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006142 goto error;
6143 }
6144 }
6145 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006146 /* mark this ruleset as unused for now */
6147 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6148 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006149 }
6150
Christopher Faulete5870d82020-04-15 11:32:03 +02006151 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006152 return ret;
6153
6154 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006155 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006156 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006157 return -1;
6158}
6159
Christopher Faulete9111b62020-04-09 18:12:08 +02006160/* Parses the "external-check" proxy keyword */
6161static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6162 struct proxy *defpx, const char *file, int line,
6163 char **errmsg)
6164{
6165 int cur_arg, ret = 0;
6166
6167 cur_arg = 1;
6168 if (!*(args[cur_arg])) {
6169 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6170 goto error;
6171 }
6172
6173 if (strcmp(args[cur_arg], "command") == 0) {
6174 if (too_many_args(2, args, errmsg, NULL))
6175 goto error;
6176 if (!*(args[cur_arg+1])) {
6177 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6178 goto error;
6179 }
6180 free(curpx->check_command);
6181 curpx->check_command = strdup(args[cur_arg+1]);
6182 }
6183 else if (strcmp(args[cur_arg], "path") == 0) {
6184 if (too_many_args(2, args, errmsg, NULL))
6185 goto error;
6186 if (!*(args[cur_arg+1])) {
6187 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6188 goto error;
6189 }
6190 free(curpx->check_path);
6191 curpx->check_path = strdup(args[cur_arg+1]);
6192 }
6193 else {
6194 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6195 args[0], args[1]);
6196 goto error;
6197 }
6198
6199 ret = (*errmsg != NULL); /* Handle warning */
6200 return ret;
6201
6202error:
6203 return -1;
6204}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006205
Christopher Faulet430e4802020-04-09 15:28:16 +02006206/* Parses the "option tcp-check" proxy keyword */
6207int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6208 const char *file, int line)
6209{
Christopher Faulet404f9192020-04-09 23:13:54 +02006210 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006211 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6212 int err_code = 0;
6213
6214 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6215 err_code |= ERR_WARN;
6216
6217 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6218 goto out;
6219
Christopher Faulet404f9192020-04-09 23:13:54 +02006220 curpx->options2 &= ~PR_O2_CHK_ANY;
6221 curpx->options2 |= PR_O2_TCPCHK_CHK;
6222
Christopher Fauletd7e63962020-04-17 20:15:59 +02006223 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006224 /* If a tcp-check rulesset is already set, do nothing */
6225 if (rules->list)
6226 goto out;
6227
6228 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6229 * get it.
6230 */
6231 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6232 goto curpx_ruleset;
6233
6234 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6235 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006236 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006237 if (rs)
6238 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006239 }
6240
Christopher Faulet404f9192020-04-09 23:13:54 +02006241 curpx_ruleset:
6242 /* Deduce the ruleset name from the proxy info */
6243 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6244 ((curpx == defpx) ? "defaults" : curpx->id),
6245 curpx->conf.file, curpx->conf.line);
6246
Christopher Faulet61cc8522020-04-20 14:54:42 +02006247 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006248 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006249 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006250 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006251 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6252 goto error;
6253 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006254 }
6255
Christopher Faulet404f9192020-04-09 23:13:54 +02006256 ruleset_found:
6257 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006258 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006259 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006260
6261 out:
6262 return err_code;
6263
6264 error:
6265 err_code |= ERR_ALERT | ERR_FATAL;
6266 goto out;
6267}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006268
6269/* Parses the "option redis-check" proxy keyword */
6270int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6271 const char *file, int line)
6272{
6273 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6274 static char *redis_res = "+PONG\r\n";
6275
6276 struct tcpcheck_ruleset *rs = NULL;
6277 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6278 struct tcpcheck_rule *chk;
6279 char *errmsg = NULL;
6280 int err_code = 0;
6281
6282 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6283 err_code |= ERR_WARN;
6284
6285 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6286 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006287
6288 curpx->options2 &= ~PR_O2_CHK_ANY;
6289 curpx->options2 |= PR_O2_TCPCHK_CHK;
6290
6291 free_tcpcheck_vars(&rules->preset_vars);
6292 rules->list = NULL;
6293 rules->flags = 0;
6294
Christopher Faulet61cc8522020-04-20 14:54:42 +02006295 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006296 if (rs)
6297 goto ruleset_found;
6298
Christopher Faulet61cc8522020-04-20 14:54:42 +02006299 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006300 if (rs == NULL) {
6301 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6302 goto error;
6303 }
6304
6305 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6306 1, curpx, &rs->rules, file, line, &errmsg);
6307 if (!chk) {
6308 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6309 goto error;
6310 }
6311 chk->index = 0;
6312 LIST_ADDQ(&rs->rules, &chk->list);
6313
6314 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6315 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006316 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006317 "on-success", "Redis server is ok",
6318 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006319 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006320 if (!chk) {
6321 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6322 goto error;
6323 }
6324 chk->index = 1;
6325 LIST_ADDQ(&rs->rules, &chk->list);
6326
Christopher Fauletd7cee712020-04-21 13:45:00 +02006327 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006328
6329 ruleset_found:
6330 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006331 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006332
6333 out:
6334 free(errmsg);
6335 return err_code;
6336
6337 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006338 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006339 err_code |= ERR_ALERT | ERR_FATAL;
6340 goto out;
6341}
6342
Christopher Faulet811f78c2020-04-01 11:10:27 +02006343
6344/* Parses the "option ssl-hello-chk" proxy keyword */
6345int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6346 const char *file, int line)
6347{
6348 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6349 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6350 *
6351 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6352 */
6353 static char sslv3_client_hello[] = {
6354 "16" /* ContentType : 0x16 = Hanshake */
6355 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6356 "0079" /* ContentLength : 0x79 bytes after this one */
6357 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6358 "000075" /* HandshakeLength : 0x75 bytes after this one */
6359 "0300" /* Hello Version : 0x0300 = v3 */
6360 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6361 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6362 "00" /* Session ID length : empty (no session ID) */
6363 "004E" /* Cipher Suite Length : 78 bytes after this one */
6364 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6365 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6366 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6367 "000D" "000E" "000F" "0010" /* various bit lengths, */
6368 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6369 "0015" "0016" "0017" "0018"
6370 "0019" "001A" "001B" "002F"
6371 "0030" "0031" "0032" "0033"
6372 "0034" "0035" "0036" "0037"
6373 "0038" "0039" "003A"
6374 "01" /* Compression Length : 0x01 = 1 byte for types */
6375 "00" /* Compression Type : 0x00 = NULL compression */
6376 };
6377
6378 struct tcpcheck_ruleset *rs = NULL;
6379 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6380 struct tcpcheck_rule *chk;
6381 char *errmsg = NULL;
6382 int err_code = 0;
6383
6384 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6385 err_code |= ERR_WARN;
6386
6387 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6388 goto out;
6389
Christopher Faulet811f78c2020-04-01 11:10:27 +02006390 curpx->options2 &= ~PR_O2_CHK_ANY;
6391 curpx->options2 |= PR_O2_TCPCHK_CHK;
6392
6393 free_tcpcheck_vars(&rules->preset_vars);
6394 rules->list = NULL;
6395 rules->flags = 0;
6396
Christopher Faulet61cc8522020-04-20 14:54:42 +02006397 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006398 if (rs)
6399 goto ruleset_found;
6400
Christopher Faulet61cc8522020-04-20 14:54:42 +02006401 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006402 if (rs == NULL) {
6403 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6404 goto error;
6405 }
6406
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006407 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006408 1, curpx, &rs->rules, file, line, &errmsg);
6409 if (!chk) {
6410 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6411 goto error;
6412 }
6413 chk->index = 0;
6414 LIST_ADDQ(&rs->rules, &chk->list);
6415
6416 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006417 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006418 "error-status", "L6RSP", "tout-status", "L6TOUT",
6419 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006420 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006421 if (!chk) {
6422 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6423 goto error;
6424 }
6425 chk->index = 1;
6426 LIST_ADDQ(&rs->rules, &chk->list);
6427
Christopher Fauletd7cee712020-04-21 13:45:00 +02006428 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006429
6430 ruleset_found:
6431 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006432 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006433
6434 out:
6435 free(errmsg);
6436 return err_code;
6437
6438 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006439 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006440 err_code |= ERR_ALERT | ERR_FATAL;
6441 goto out;
6442}
6443
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006444/* Parses the "option smtpchk" proxy keyword */
6445int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6446 const char *file, int line)
6447{
6448 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6449
6450 struct tcpcheck_ruleset *rs = NULL;
6451 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6452 struct tcpcheck_rule *chk;
6453 struct tcpcheck_var *var = NULL;
6454 char *cmd = NULL, *errmsg = NULL;
6455 int err_code = 0;
6456
6457 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6458 err_code |= ERR_WARN;
6459
6460 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6461 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006462
6463 curpx->options2 &= ~PR_O2_CHK_ANY;
6464 curpx->options2 |= PR_O2_TCPCHK_CHK;
6465
6466 free_tcpcheck_vars(&rules->preset_vars);
6467 rules->list = NULL;
6468 rules->flags = 0;
6469
6470 cur_arg += 2;
6471 if (*args[cur_arg] && *args[cur_arg+1] &&
6472 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6473 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6474 if (cmd)
6475 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6476 }
6477 else {
6478 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6479 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6480 cmd = strdup("HELO localhost");
6481 }
6482
Christopher Fauletb61caf42020-04-21 10:57:42 +02006483 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006484 if (cmd == NULL || var == NULL) {
6485 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6486 goto error;
6487 }
6488 var->data.type = SMP_T_STR;
6489 var->data.u.str.area = cmd;
6490 var->data.u.str.data = strlen(cmd);
6491 LIST_INIT(&var->list);
6492 LIST_ADDQ(&rules->preset_vars, &var->list);
6493 cmd = NULL;
6494 var = NULL;
6495
Christopher Faulet61cc8522020-04-20 14:54:42 +02006496 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006497 if (rs)
6498 goto ruleset_found;
6499
Christopher Faulet61cc8522020-04-20 14:54:42 +02006500 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006501 if (rs == NULL) {
6502 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6503 goto error;
6504 }
6505
6506 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6507 1, curpx, &rs->rules, file, line, &errmsg);
6508 if (!chk) {
6509 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6510 goto error;
6511 }
6512 chk->index = 0;
6513 LIST_ADDQ(&rs->rules, &chk->list);
6514
6515 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6516 "min-recv", "4",
6517 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006518 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006519 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006520 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006521 if (!chk) {
6522 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6523 goto error;
6524 }
6525 chk->index = 1;
6526 LIST_ADDQ(&rs->rules, &chk->list);
6527
6528 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6529 "min-recv", "4",
6530 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006531 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6532 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006533 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006534 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006535 if (!chk) {
6536 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6537 goto error;
6538 }
6539 chk->index = 2;
6540 LIST_ADDQ(&rs->rules, &chk->list);
6541
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006542 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006543 1, curpx, &rs->rules, file, line, &errmsg);
6544 if (!chk) {
6545 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6546 goto error;
6547 }
6548 chk->index = 3;
6549 LIST_ADDQ(&rs->rules, &chk->list);
6550
6551 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6552 "min-recv", "4",
6553 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006554 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6555 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6556 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006557 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006558 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006559 if (!chk) {
6560 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6561 goto error;
6562 }
6563 chk->index = 4;
6564 LIST_ADDQ(&rs->rules, &chk->list);
6565
Christopher Fauletd7cee712020-04-21 13:45:00 +02006566 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006567
6568 ruleset_found:
6569 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006570 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006571
6572 out:
6573 free(errmsg);
6574 return err_code;
6575
6576 error:
6577 free(cmd);
6578 free(var);
6579 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006580 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006581 err_code |= ERR_ALERT | ERR_FATAL;
6582 goto out;
6583}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006584
Christopher Fauletce355072020-04-02 11:44:39 +02006585/* Parses the "option pgsql-check" proxy keyword */
6586int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6587 const char *file, int line)
6588{
6589 static char pgsql_req[] = {
6590 "%[var(check.plen),htonl,hex]" /* The packet length*/
6591 "00030000" /* the version 3.0 */
6592 "7573657200" /* "user" key */
6593 "%[var(check.username),hex]00" /* the username */
6594 "00"
6595 };
6596
6597 struct tcpcheck_ruleset *rs = NULL;
6598 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6599 struct tcpcheck_rule *chk;
6600 struct tcpcheck_var *var = NULL;
6601 char *user = NULL, *errmsg = NULL;
6602 size_t packetlen = 0;
6603 int err_code = 0;
6604
6605 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6606 err_code |= ERR_WARN;
6607
6608 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6609 goto out;
6610
Christopher Fauletce355072020-04-02 11:44:39 +02006611 curpx->options2 &= ~PR_O2_CHK_ANY;
6612 curpx->options2 |= PR_O2_TCPCHK_CHK;
6613
6614 free_tcpcheck_vars(&rules->preset_vars);
6615 rules->list = NULL;
6616 rules->flags = 0;
6617
6618 cur_arg += 2;
6619 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6620 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6621 file, line, args[0], args[1]);
6622 goto error;
6623 }
6624 if (strcmp(args[cur_arg], "user") == 0) {
6625 packetlen = 15 + strlen(args[cur_arg+1]);
6626 user = strdup(args[cur_arg+1]);
6627
Christopher Fauletb61caf42020-04-21 10:57:42 +02006628 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006629 if (user == NULL || var == NULL) {
6630 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6631 goto error;
6632 }
6633 var->data.type = SMP_T_STR;
6634 var->data.u.str.area = user;
6635 var->data.u.str.data = strlen(user);
6636 LIST_INIT(&var->list);
6637 LIST_ADDQ(&rules->preset_vars, &var->list);
6638 user = NULL;
6639 var = NULL;
6640
Christopher Fauletb61caf42020-04-21 10:57:42 +02006641 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006642 if (var == NULL) {
6643 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6644 goto error;
6645 }
6646 var->data.type = SMP_T_SINT;
6647 var->data.u.sint = packetlen;
6648 LIST_INIT(&var->list);
6649 LIST_ADDQ(&rules->preset_vars, &var->list);
6650 var = NULL;
6651 }
6652 else {
6653 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6654 file, line, args[0], args[1]);
6655 goto error;
6656 }
6657
Christopher Faulet61cc8522020-04-20 14:54:42 +02006658 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006659 if (rs)
6660 goto ruleset_found;
6661
Christopher Faulet61cc8522020-04-20 14:54:42 +02006662 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006663 if (rs == NULL) {
6664 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6665 goto error;
6666 }
6667
6668 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6669 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 = 0;
6675 LIST_ADDQ(&rs->rules, &chk->list);
6676
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006677 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006678 1, curpx, &rs->rules, file, line, &errmsg);
6679 if (!chk) {
6680 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6681 goto error;
6682 }
6683 chk->index = 1;
6684 LIST_ADDQ(&rs->rules, &chk->list);
6685
6686 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6687 "min-recv", "5",
6688 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006689 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006690 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006691 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006692 if (!chk) {
6693 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6694 goto error;
6695 }
6696 chk->index = 2;
6697 LIST_ADDQ(&rs->rules, &chk->list);
6698
Christopher Fauletb841c742020-04-27 18:29:49 +02006699 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 +02006700 "min-recv", "9",
6701 "error-status", "L7STS",
6702 "on-success", "PostgreSQL server is ok",
6703 "on-error", "PostgreSQL unknown error",
6704 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006705 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006706 if (!chk) {
6707 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6708 goto error;
6709 }
6710 chk->index = 3;
6711 LIST_ADDQ(&rs->rules, &chk->list);
6712
Christopher Fauletd7cee712020-04-21 13:45:00 +02006713 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletce355072020-04-02 11:44:39 +02006714
6715 ruleset_found:
6716 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006717 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006718
6719 out:
6720 free(errmsg);
6721 return err_code;
6722
6723 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006724 free(user);
6725 free(var);
6726 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006727 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006728 err_code |= ERR_ALERT | ERR_FATAL;
6729 goto out;
6730}
6731
6732
6733/* Parses the "option mysql-check" proxy keyword */
6734int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6735 const char *file, int line)
6736{
6737 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6738 * const char mysql40_client_auth_pkt[] = {
6739 * "\x0e\x00\x00" // packet length
6740 * "\x01" // packet number
6741 * "\x00\x00" // client capabilities
6742 * "\x00\x00\x01" // max packet
6743 * "haproxy\x00" // username (null terminated string)
6744 * "\x00" // filler (always 0x00)
6745 * "\x01\x00\x00" // packet length
6746 * "\x00" // packet number
6747 * "\x01" // COM_QUIT command
6748 * };
6749 */
6750 static char mysql40_rsname[] = "*mysql40-check";
6751 static char mysql40_req[] = {
6752 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6753 "0080" /* client capabilities */
6754 "000001" /* max packet */
6755 "%[var(check.username),hex]00" /* the username */
6756 "00" /* filler (always 0x00) */
6757 "010000" /* packet length*/
6758 "00" /* sequence ID */
6759 "01" /* COM_QUIT command */
6760 };
6761
6762 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6763 * const char mysql41_client_auth_pkt[] = {
6764 * "\x0e\x00\x00\" // packet length
6765 * "\x01" // packet number
6766 * "\x00\x00\x00\x00" // client capabilities
6767 * "\x00\x00\x00\x01" // max packet
6768 * "\x21" // character set (UTF-8)
6769 * char[23] // All zeroes
6770 * "haproxy\x00" // username (null terminated string)
6771 * "\x00" // filler (always 0x00)
6772 * "\x01\x00\x00" // packet length
6773 * "\x00" // packet number
6774 * "\x01" // COM_QUIT command
6775 * };
6776 */
6777 static char mysql41_rsname[] = "*mysql41-check";
6778 static char mysql41_req[] = {
6779 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6780 "00820000" /* client capabilities */
6781 "00800001" /* max packet */
6782 "21" /* character set (UTF-8) */
6783 "000000000000000000000000" /* 23 bytes, al zeroes */
6784 "0000000000000000000000"
6785 "%[var(check.username),hex]00" /* the username */
6786 "00" /* filler (always 0x00) */
6787 "010000" /* packet length*/
6788 "00" /* sequence ID */
6789 "01" /* COM_QUIT command */
6790 };
6791
6792 struct tcpcheck_ruleset *rs = NULL;
6793 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6794 struct tcpcheck_rule *chk;
6795 struct tcpcheck_var *var = NULL;
6796 char *mysql_rsname = "*mysql-check";
6797 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6798 int index = 0, err_code = 0;
6799
6800 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6801 err_code |= ERR_WARN;
6802
6803 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6804 goto out;
6805
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006806 curpx->options2 &= ~PR_O2_CHK_ANY;
6807 curpx->options2 |= PR_O2_TCPCHK_CHK;
6808
6809 free_tcpcheck_vars(&rules->preset_vars);
6810 rules->list = NULL;
6811 rules->flags = 0;
6812
6813 cur_arg += 2;
6814 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006815 int packetlen, userlen;
6816
6817 if (strcmp(args[cur_arg], "user") != 0) {
6818 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6819 file, line, args[0], args[1], args[cur_arg]);
6820 goto error;
6821 }
6822
6823 if (*(args[cur_arg+1]) == 0) {
6824 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6825 file, line, args[0], args[1], args[cur_arg]);
6826 goto error;
6827 }
6828
6829 hdr = calloc(4, sizeof(*hdr));
6830 user = strdup(args[cur_arg+1]);
6831 userlen = strlen(args[cur_arg+1]);
6832
6833 if (hdr == NULL || user == NULL) {
6834 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6835 goto error;
6836 }
6837
6838 if (*args[cur_arg+2]) {
6839 if (strcmp(args[cur_arg+2], "post-41") != 0) {
6840 ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41' (got '%s').\n",
6841 file, line, args[cur_arg], args[cur_arg+2]);
6842 goto error;
6843 }
6844 packetlen = userlen + 7 + 27;
6845 mysql_req = mysql41_req;
6846 mysql_rsname = mysql41_rsname;
6847 }
6848 else {
6849 packetlen = userlen + 7;
6850 mysql_req = mysql40_req;
6851 mysql_rsname = mysql40_rsname;
6852 }
6853
6854 hdr[0] = (unsigned char)(packetlen & 0xff);
6855 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6856 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6857 hdr[3] = 1;
6858
Christopher Fauletb61caf42020-04-21 10:57:42 +02006859 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006860 if (var == NULL) {
6861 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6862 goto error;
6863 }
6864 var->data.type = SMP_T_STR;
6865 var->data.u.str.area = hdr;
6866 var->data.u.str.data = 4;
6867 LIST_INIT(&var->list);
6868 LIST_ADDQ(&rules->preset_vars, &var->list);
6869 hdr = NULL;
6870 var = NULL;
6871
Christopher Fauletb61caf42020-04-21 10:57:42 +02006872 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006873 if (var == NULL) {
6874 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6875 goto error;
6876 }
6877 var->data.type = SMP_T_STR;
6878 var->data.u.str.area = user;
6879 var->data.u.str.data = strlen(user);
6880 LIST_INIT(&var->list);
6881 LIST_ADDQ(&rules->preset_vars, &var->list);
6882 user = NULL;
6883 var = NULL;
6884 }
6885
Christopher Faulet61cc8522020-04-20 14:54:42 +02006886 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006887 if (rs)
6888 goto ruleset_found;
6889
Christopher Faulet61cc8522020-04-20 14:54:42 +02006890 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006891 if (rs == NULL) {
6892 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6893 goto error;
6894 }
6895
6896 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6897 1, curpx, &rs->rules, file, line, &errmsg);
6898 if (!chk) {
6899 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6900 goto error;
6901 }
6902 chk->index = index++;
6903 LIST_ADDQ(&rs->rules, &chk->list);
6904
6905 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006906 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006907 1, curpx, &rs->rules, file, line, &errmsg);
6908 if (!chk) {
6909 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6910 goto error;
6911 }
6912 chk->index = index++;
6913 LIST_ADDQ(&rs->rules, &chk->list);
6914 }
6915
6916 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006917 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006918 if (!chk) {
6919 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6920 goto error;
6921 }
6922 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6923 chk->index = index++;
6924 LIST_ADDQ(&rs->rules, &chk->list);
6925
6926 if (mysql_req) {
6927 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006928 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006929 if (!chk) {
6930 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6931 goto error;
6932 }
6933 chk->expect.custom = tcpcheck_mysql_expect_ok;
6934 chk->index = index++;
6935 LIST_ADDQ(&rs->rules, &chk->list);
6936 }
6937
Christopher Fauletd7cee712020-04-21 13:45:00 +02006938 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006939
6940 ruleset_found:
6941 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006942 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006943
6944 out:
6945 free(errmsg);
6946 return err_code;
6947
6948 error:
6949 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006950 free(user);
6951 free(var);
6952 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006953 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006954 err_code |= ERR_ALERT | ERR_FATAL;
6955 goto out;
6956}
6957
Christopher Faulet1997eca2020-04-03 23:13:50 +02006958int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6959 const char *file, int line)
6960{
6961 static char *ldap_req = "300C020101600702010304008000";
6962
6963 struct tcpcheck_ruleset *rs = NULL;
6964 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6965 struct tcpcheck_rule *chk;
6966 char *errmsg = NULL;
6967 int err_code = 0;
6968
6969 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6970 err_code |= ERR_WARN;
6971
6972 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6973 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006974
6975 curpx->options2 &= ~PR_O2_CHK_ANY;
6976 curpx->options2 |= PR_O2_TCPCHK_CHK;
6977
6978 free_tcpcheck_vars(&rules->preset_vars);
6979 rules->list = NULL;
6980 rules->flags = 0;
6981
Christopher Faulet61cc8522020-04-20 14:54:42 +02006982 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006983 if (rs)
6984 goto ruleset_found;
6985
Christopher Faulet61cc8522020-04-20 14:54:42 +02006986 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006987 if (rs == NULL) {
6988 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6989 goto error;
6990 }
6991
6992 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6993 1, curpx, &rs->rules, file, line, &errmsg);
6994 if (!chk) {
6995 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6996 goto error;
6997 }
6998 chk->index = 0;
6999 LIST_ADDQ(&rs->rules, &chk->list);
7000
7001 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
7002 "min-recv", "14",
7003 "on-error", "Not LDAPv3 protocol",
7004 ""},
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->index = 1;
7011 LIST_ADDQ(&rs->rules, &chk->list);
7012
7013 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007014 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007015 if (!chk) {
7016 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7017 goto error;
7018 }
7019 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
7020 chk->index = 2;
7021 LIST_ADDQ(&rs->rules, &chk->list);
7022
Christopher Fauletd7cee712020-04-21 13:45:00 +02007023 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007024
7025 ruleset_found:
7026 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007027 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02007028
7029 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02007030 free(errmsg);
7031 return err_code;
7032
7033 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007034 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007035 err_code |= ERR_ALERT | ERR_FATAL;
7036 goto out;
7037}
7038
7039int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7040 const char *file, int line)
7041{
7042 struct tcpcheck_ruleset *rs = NULL;
7043 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7044 struct tcpcheck_rule *chk;
7045 char *spop_req = NULL;
7046 char *errmsg = NULL;
7047 int spop_len = 0, err_code = 0;
7048
7049 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7050 err_code |= ERR_WARN;
7051
7052 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7053 goto out;
7054
Christopher Faulet267b01b2020-04-04 10:27:09 +02007055 curpx->options2 &= ~PR_O2_CHK_ANY;
7056 curpx->options2 |= PR_O2_TCPCHK_CHK;
7057
7058 free_tcpcheck_vars(&rules->preset_vars);
7059 rules->list = NULL;
7060 rules->flags = 0;
7061
7062
Christopher Faulet61cc8522020-04-20 14:54:42 +02007063 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007064 if (rs)
7065 goto ruleset_found;
7066
Christopher Faulet61cc8522020-04-20 14:54:42 +02007067 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007068 if (rs == NULL) {
7069 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7070 goto error;
7071 }
7072
7073 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7074 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7075 goto error;
7076 }
7077 chunk_reset(&trash);
7078 dump_binary(&trash, spop_req, spop_len);
7079 trash.area[trash.data] = '\0';
7080
7081 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7082 1, curpx, &rs->rules, file, line, &errmsg);
7083 if (!chk) {
7084 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7085 goto error;
7086 }
7087 chk->index = 0;
7088 LIST_ADDQ(&rs->rules, &chk->list);
7089
7090 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007091 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007092 if (!chk) {
7093 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7094 goto error;
7095 }
7096 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7097 chk->index = 1;
7098 LIST_ADDQ(&rs->rules, &chk->list);
7099
Christopher Fauletd7cee712020-04-21 13:45:00 +02007100 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007101
7102 ruleset_found:
7103 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007104 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007105
7106 out:
7107 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007108 free(errmsg);
7109 return err_code;
7110
7111 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007112 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007113 err_code |= ERR_ALERT | ERR_FATAL;
7114 goto out;
7115}
Christopher Fauletce355072020-04-02 11:44:39 +02007116
Christopher Faulete5870d82020-04-15 11:32:03 +02007117
7118struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7119{
7120 struct tcpcheck_rule *chk = NULL;
7121 struct tcpcheck_http_hdr *hdr = NULL;
7122 char *meth = NULL, *uri = NULL, *vsn = NULL;
7123 char *hdrs, *body;
7124
7125 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7126 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7127 if (hdrs == body)
7128 hdrs = NULL;
7129 if (hdrs) {
7130 *hdrs = '\0';
7131 hdrs +=2;
7132 }
7133 if (body) {
7134 *body = '\0';
7135 body += 4;
7136 }
7137 if (hdrs || body) {
7138 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7139 " Please, consider to use 'http-check send' directive instead.");
7140 }
7141
7142 chk = calloc(1, sizeof(*chk));
7143 if (!chk) {
7144 memprintf(errmsg, "out of memory");
7145 goto error;
7146 }
7147 chk->action = TCPCHK_ACT_SEND;
7148 chk->send.type = TCPCHK_SEND_HTTP;
7149 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7150 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7151 LIST_INIT(&chk->send.http.hdrs);
7152
7153 /* Copy the method, uri and version */
7154 if (*args[cur_arg]) {
7155 if (!*args[cur_arg+1])
7156 uri = args[cur_arg];
7157 else
7158 meth = args[cur_arg];
7159 }
7160 if (*args[cur_arg+1])
7161 uri = args[cur_arg+1];
7162 if (*args[cur_arg+2])
7163 vsn = args[cur_arg+2];
7164
7165 if (meth) {
7166 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7167 chk->send.http.meth.str.area = strdup(meth);
7168 chk->send.http.meth.str.data = strlen(meth);
7169 if (!chk->send.http.meth.str.area) {
7170 memprintf(errmsg, "out of memory");
7171 goto error;
7172 }
7173 }
7174 if (uri) {
7175 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007176 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007177 memprintf(errmsg, "out of memory");
7178 goto error;
7179 }
7180 }
7181 if (vsn) {
7182 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007183 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007184 memprintf(errmsg, "out of memory");
7185 goto error;
7186 }
7187 }
7188
7189 /* Copy the header */
7190 if (hdrs) {
7191 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7192 struct h1m h1m;
7193 int i, ret;
7194
7195 /* Build and parse the request */
7196 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7197
7198 h1m.flags = H1_MF_HDRS_ONLY;
7199 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7200 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7201 &h1m, NULL);
7202 if (ret <= 0) {
7203 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7204 goto error;
7205 }
7206
Christopher Fauletb61caf42020-04-21 10:57:42 +02007207 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007208 hdr = calloc(1, sizeof(*hdr));
7209 if (!hdr) {
7210 memprintf(errmsg, "out of memory");
7211 goto error;
7212 }
7213 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007214 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007215 if (!hdr->name.ptr) {
7216 memprintf(errmsg, "out of memory");
7217 goto error;
7218 }
7219
Christopher Fauletb61caf42020-04-21 10:57:42 +02007220 ist0(tmp_hdrs[i].v);
7221 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 +02007222 goto error;
7223 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7224 }
7225 }
7226
7227 /* Copy the body */
7228 if (body) {
7229 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007230 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007231 memprintf(errmsg, "out of memory");
7232 goto error;
7233 }
7234 }
7235
7236 return chk;
7237
7238 error:
7239 free_tcpcheck_http_hdr(hdr);
7240 free_tcpcheck(chk, 0);
7241 return NULL;
7242}
7243
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007244int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7245 const char *file, int line)
7246{
Christopher Faulete5870d82020-04-15 11:32:03 +02007247 struct tcpcheck_ruleset *rs = NULL;
7248 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7249 struct tcpcheck_rule *chk;
7250 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007251 int err_code = 0;
7252
7253 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7254 err_code |= ERR_WARN;
7255
7256 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7257 goto out;
7258
Christopher Faulete5870d82020-04-15 11:32:03 +02007259 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7260 if (!chk) {
7261 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7262 goto error;
7263 }
7264 if (errmsg) {
7265 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7266 err_code |= ERR_WARN;
7267 free(errmsg);
7268 errmsg = NULL;
7269 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007270
Christopher Faulete5870d82020-04-15 11:32:03 +02007271 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007272 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007273 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007274
Christopher Faulete5870d82020-04-15 11:32:03 +02007275 free_tcpcheck_vars(&rules->preset_vars);
7276 rules->list = NULL;
7277 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007278
Christopher Faulete5870d82020-04-15 11:32:03 +02007279 /* Deduce the ruleset name from the proxy info */
7280 chunk_printf(&trash, "*http-check-%s_%s-%d",
7281 ((curpx == defpx) ? "defaults" : curpx->id),
7282 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007283
Christopher Faulet61cc8522020-04-20 14:54:42 +02007284 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007285 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007286 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007287 if (rs == NULL) {
7288 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
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
Christopher Faulete5870d82020-04-15 11:32:03 +02007293 rules->list = &rs->rules;
7294 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7295 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7296 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7297 rules->list = NULL;
7298 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007299 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007300
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007301 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007302 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007303 return err_code;
7304
7305 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007306 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007307 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007308 err_code |= ERR_ALERT | ERR_FATAL;
7309 goto out;
7310}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007311
Christopher Faulet6f557912020-04-09 15:58:50 +02007312int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7313 const char *file, int line)
7314{
7315 int err_code = 0;
7316
Christopher Faulet6f557912020-04-09 15:58:50 +02007317 curpx->options2 &= ~PR_O2_CHK_ANY;
7318 curpx->options2 |= PR_O2_EXT_CHK;
7319 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7320 goto out;
7321
7322 out:
7323 return err_code;
7324}
7325
Christopher Fauletce8111e2020-04-06 15:04:11 +02007326/* Parse the "addr" server keyword */
7327static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7328 char **errmsg)
7329{
7330 struct sockaddr_storage *sk;
7331 struct protocol *proto;
7332 int port1, port2, err_code = 0;
7333
7334
7335 if (!*args[*cur_arg+1]) {
7336 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7337 goto error;
7338 }
7339
7340 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7341 if (!sk) {
7342 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7343 goto error;
7344 }
7345
7346 proto = protocol_by_family(sk->ss_family);
7347 if (!proto || !proto->connect) {
7348 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7349 args[*cur_arg], args[*cur_arg+1]);
7350 goto error;
7351 }
7352
7353 if (port1 != port2) {
7354 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7355 args[*cur_arg], args[*cur_arg+1]);
7356 goto error;
7357 }
7358
7359 srv->check.addr = srv->agent.addr = *sk;
7360 srv->flags |= SRV_F_CHECKADDR;
7361 srv->flags |= SRV_F_AGENTADDR;
7362
7363 out:
7364 return err_code;
7365
7366 error:
7367 err_code |= ERR_ALERT | ERR_FATAL;
7368 goto out;
7369}
7370
7371
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007372/* Parse the "agent-addr" server keyword */
7373static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7374 char **errmsg)
7375{
7376 int err_code = 0;
7377
7378 if (!*(args[*cur_arg+1])) {
7379 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7380 goto error;
7381 }
7382 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7383 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7384 goto error;
7385 }
7386
7387 out:
7388 return err_code;
7389
7390 error:
7391 err_code |= ERR_ALERT | ERR_FATAL;
7392 goto out;
7393}
7394
7395/* Parse the "agent-check" server keyword */
7396static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7397 char **errmsg)
7398{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007399 struct tcpcheck_ruleset *rs = NULL;
7400 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7401 struct tcpcheck_rule *chk;
7402 int err_code = 0;
7403
7404 if (srv->do_agent)
7405 goto out;
7406
7407 if (!rules) {
7408 rules = calloc(1, sizeof(*rules));
7409 if (!rules) {
7410 memprintf(errmsg, "out of memory.");
7411 goto error;
7412 }
7413 LIST_INIT(&rules->preset_vars);
7414 srv->agent.tcpcheck_rules = rules;
7415 }
7416 rules->list = NULL;
7417 rules->flags = 0;
7418
Christopher Faulet61cc8522020-04-20 14:54:42 +02007419 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007420 if (rs)
7421 goto ruleset_found;
7422
Christopher Faulet61cc8522020-04-20 14:54:42 +02007423 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007424 if (rs == NULL) {
7425 memprintf(errmsg, "out of memory.");
7426 goto error;
7427 }
7428
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007429 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007430 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7431 if (!chk) {
7432 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7433 goto error;
7434 }
7435 chk->index = 0;
7436 LIST_ADDQ(&rs->rules, &chk->list);
7437
7438 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007439 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7440 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007441 if (!chk) {
7442 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7443 goto error;
7444 }
7445 chk->expect.custom = tcpcheck_agent_expect_reply;
7446 chk->index = 1;
7447 LIST_ADDQ(&rs->rules, &chk->list);
7448
Christopher Fauletd7cee712020-04-21 13:45:00 +02007449 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007450
7451 ruleset_found:
7452 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007453 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007454 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007455
7456 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007457 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007458
7459 error:
7460 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007461 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007462 err_code |= ERR_ALERT | ERR_FATAL;
7463 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007464}
7465
7466/* Parse the "agent-inter" server keyword */
7467static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7468 char **errmsg)
7469{
7470 const char *err = NULL;
7471 unsigned int delay;
7472 int err_code = 0;
7473
7474 if (!*(args[*cur_arg+1])) {
7475 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7476 goto error;
7477 }
7478
7479 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7480 if (err == PARSE_TIME_OVER) {
7481 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7482 args[*cur_arg+1], args[*cur_arg], srv->id);
7483 goto error;
7484 }
7485 else if (err == PARSE_TIME_UNDER) {
7486 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7487 args[*cur_arg+1], args[*cur_arg], srv->id);
7488 goto error;
7489 }
7490 else if (err) {
7491 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7492 *err, srv->id);
7493 goto error;
7494 }
7495 if (delay <= 0) {
7496 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7497 delay, args[*cur_arg], srv->id);
7498 goto error;
7499 }
7500 srv->agent.inter = delay;
7501
7502 out:
7503 return err_code;
7504
7505 error:
7506 err_code |= ERR_ALERT | ERR_FATAL;
7507 goto out;
7508}
7509
7510/* Parse the "agent-port" server keyword */
7511static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7512 char **errmsg)
7513{
7514 int err_code = 0;
7515
7516 if (!*(args[*cur_arg+1])) {
7517 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7518 goto error;
7519 }
7520
7521 global.maxsock++;
7522 srv->agent.port = atol(args[*cur_arg+1]);
7523
7524 out:
7525 return err_code;
7526
7527 error:
7528 err_code |= ERR_ALERT | ERR_FATAL;
7529 goto out;
7530}
7531
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007532int set_srv_agent_send(struct server *srv, const char *send)
7533{
7534 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7535 struct tcpcheck_var *var = NULL;
7536 char *str;
7537
7538 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007539 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007540 if (str == NULL || var == NULL)
7541 goto error;
7542
7543 free_tcpcheck_vars(&rules->preset_vars);
7544
7545 var->data.type = SMP_T_STR;
7546 var->data.u.str.area = str;
7547 var->data.u.str.data = strlen(str);
7548 LIST_INIT(&var->list);
7549 LIST_ADDQ(&rules->preset_vars, &var->list);
7550
7551 return 1;
7552
7553 error:
7554 free(str);
7555 free(var);
7556 return 0;
7557}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007558
7559/* Parse the "agent-send" server keyword */
7560static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7561 char **errmsg)
7562{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007563 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007564 int err_code = 0;
7565
7566 if (!*(args[*cur_arg+1])) {
7567 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7568 goto error;
7569 }
7570
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007571 if (!rules) {
7572 rules = calloc(1, sizeof(*rules));
7573 if (!rules) {
7574 memprintf(errmsg, "out of memory.");
7575 goto error;
7576 }
7577 LIST_INIT(&rules->preset_vars);
7578 srv->agent.tcpcheck_rules = rules;
7579 }
7580
7581 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007582 memprintf(errmsg, "out of memory.");
7583 goto error;
7584 }
7585
7586 out:
7587 return err_code;
7588
7589 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007590 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007591 err_code |= ERR_ALERT | ERR_FATAL;
7592 goto out;
7593}
7594
7595/* Parse the "no-agent-send" server keyword */
7596static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7597 char **errmsg)
7598{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007599 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007600 return 0;
7601}
7602
Christopher Fauletce8111e2020-04-06 15:04:11 +02007603/* Parse the "check" server keyword */
7604static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7605 char **errmsg)
7606{
7607 srv->do_check = 1;
7608 return 0;
7609}
7610
7611/* Parse the "check-send-proxy" server keyword */
7612static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7613 char **errmsg)
7614{
7615 srv->check.send_proxy = 1;
7616 return 0;
7617}
7618
7619/* Parse the "check-via-socks4" server keyword */
7620static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7621 char **errmsg)
7622{
7623 srv->check.via_socks4 = 1;
7624 return 0;
7625}
7626
7627/* Parse the "no-check" server keyword */
7628static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7629 char **errmsg)
7630{
7631 deinit_srv_check(srv);
7632 return 0;
7633}
7634
7635/* Parse the "no-check-send-proxy" server keyword */
7636static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7637 char **errmsg)
7638{
7639 srv->check.send_proxy = 0;
7640 return 0;
7641}
7642
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007643/* parse the "check-proto" server keyword */
7644static int srv_parse_check_proto(char **args, int *cur_arg,
7645 struct proxy *px, struct server *newsrv, char **err)
7646{
7647 int err_code = 0;
7648
7649 if (!*args[*cur_arg + 1]) {
7650 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7651 goto error;
7652 }
7653 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7654 if (!newsrv->check.mux_proto) {
7655 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7656 goto error;
7657 }
7658
7659 out:
7660 return err_code;
7661
7662 error:
7663 err_code |= ERR_ALERT | ERR_FATAL;
7664 goto out;
7665}
7666
7667
Christopher Fauletce8111e2020-04-06 15:04:11 +02007668/* Parse the "rise" server keyword */
7669static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7670 char **errmsg)
7671{
7672 int err_code = 0;
7673
7674 if (!*args[*cur_arg + 1]) {
7675 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7676 goto error;
7677 }
7678
7679 srv->check.rise = atol(args[*cur_arg+1]);
7680 if (srv->check.rise <= 0) {
7681 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7682 goto error;
7683 }
7684
7685 if (srv->check.health)
7686 srv->check.health = srv->check.rise;
7687
7688 out:
7689 return err_code;
7690
7691 error:
7692 deinit_srv_agent_check(srv);
7693 err_code |= ERR_ALERT | ERR_FATAL;
7694 goto out;
7695 return 0;
7696}
7697
7698/* Parse the "fall" server keyword */
7699static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7700 char **errmsg)
7701{
7702 int err_code = 0;
7703
7704 if (!*args[*cur_arg + 1]) {
7705 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7706 goto error;
7707 }
7708
7709 srv->check.fall = atol(args[*cur_arg+1]);
7710 if (srv->check.fall <= 0) {
7711 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7712 goto error;
7713 }
7714
7715 out:
7716 return err_code;
7717
7718 error:
7719 deinit_srv_agent_check(srv);
7720 err_code |= ERR_ALERT | ERR_FATAL;
7721 goto out;
7722 return 0;
7723}
7724
7725/* Parse the "inter" server keyword */
7726static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7727 char **errmsg)
7728{
7729 const char *err = NULL;
7730 unsigned int delay;
7731 int err_code = 0;
7732
7733 if (!*(args[*cur_arg+1])) {
7734 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7735 goto error;
7736 }
7737
7738 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7739 if (err == PARSE_TIME_OVER) {
7740 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7741 args[*cur_arg+1], args[*cur_arg], srv->id);
7742 goto error;
7743 }
7744 else if (err == PARSE_TIME_UNDER) {
7745 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7746 args[*cur_arg+1], args[*cur_arg], srv->id);
7747 goto error;
7748 }
7749 else if (err) {
7750 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7751 *err, srv->id);
7752 goto error;
7753 }
7754 if (delay <= 0) {
7755 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7756 delay, args[*cur_arg], srv->id);
7757 goto error;
7758 }
7759 srv->check.inter = delay;
7760
7761 out:
7762 return err_code;
7763
7764 error:
7765 err_code |= ERR_ALERT | ERR_FATAL;
7766 goto out;
7767}
7768
7769
7770/* Parse the "fastinter" server keyword */
7771static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7772 char **errmsg)
7773{
7774 const char *err = NULL;
7775 unsigned int delay;
7776 int err_code = 0;
7777
7778 if (!*(args[*cur_arg+1])) {
7779 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7780 goto error;
7781 }
7782
7783 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7784 if (err == PARSE_TIME_OVER) {
7785 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7786 args[*cur_arg+1], args[*cur_arg], srv->id);
7787 goto error;
7788 }
7789 else if (err == PARSE_TIME_UNDER) {
7790 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7791 args[*cur_arg+1], args[*cur_arg], srv->id);
7792 goto error;
7793 }
7794 else if (err) {
7795 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7796 *err, srv->id);
7797 goto error;
7798 }
7799 if (delay <= 0) {
7800 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7801 delay, args[*cur_arg], srv->id);
7802 goto error;
7803 }
7804 srv->check.fastinter = delay;
7805
7806 out:
7807 return err_code;
7808
7809 error:
7810 err_code |= ERR_ALERT | ERR_FATAL;
7811 goto out;
7812}
7813
7814
7815/* Parse the "downinter" server keyword */
7816static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7817 char **errmsg)
7818{
7819 const char *err = NULL;
7820 unsigned int delay;
7821 int err_code = 0;
7822
7823 if (!*(args[*cur_arg+1])) {
7824 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7825 goto error;
7826 }
7827
7828 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7829 if (err == PARSE_TIME_OVER) {
7830 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7831 args[*cur_arg+1], args[*cur_arg], srv->id);
7832 goto error;
7833 }
7834 else if (err == PARSE_TIME_UNDER) {
7835 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7836 args[*cur_arg+1], args[*cur_arg], srv->id);
7837 goto error;
7838 }
7839 else if (err) {
7840 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7841 *err, srv->id);
7842 goto error;
7843 }
7844 if (delay <= 0) {
7845 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7846 delay, args[*cur_arg], srv->id);
7847 goto error;
7848 }
7849 srv->check.downinter = delay;
7850
7851 out:
7852 return err_code;
7853
7854 error:
7855 err_code |= ERR_ALERT | ERR_FATAL;
7856 goto out;
7857}
7858
7859/* Parse the "port" server keyword */
7860static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7861 char **errmsg)
7862{
7863 int err_code = 0;
7864
7865 if (!*(args[*cur_arg+1])) {
7866 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7867 goto error;
7868 }
7869
7870 global.maxsock++;
7871 srv->check.port = atol(args[*cur_arg+1]);
7872 srv->flags |= SRV_F_CHECKPORT;
7873
7874 out:
7875 return err_code;
7876
7877 error:
7878 err_code |= ERR_ALERT | ERR_FATAL;
7879 goto out;
7880}
7881
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007882static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007883 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7884 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7885 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007886 { 0, NULL, NULL },
7887}};
7888
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007889static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007890 { "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 +02007891 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7892 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7893 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7894 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7895 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007896 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007897 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007898 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7899 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007900 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007901 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7902 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7903 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7904 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7905 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7906 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7907 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7908 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007909 { NULL, NULL, 0 },
7910}};
7911
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007912INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007913INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007914
Willy Tarreaubd741542010-03-16 18:46:54 +01007915/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007916 * Local variables:
7917 * c-indent-level: 8
7918 * c-basic-offset: 8
7919 * End:
7920 */