blob: 6744e2d3c40bdd6477aa60a497b10d892c4e4be1 [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
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020033#include <haproxy/api.h>
Christopher Fauletfd6c2292020-03-25 18:20:15 +010034#include <common/cfgparse.h>
Willy Tarreauc7e42382012-08-24 19:22:53 +020035#include <common/chunk.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020036#include <haproxy/list.h>
Willy Tarreau83749182007-04-15 20:56:27 +020037#include <common/standard.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020038#include <haproxy/time.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020039#include <haproxy/thread.h>
Christopher Faulete5870d82020-04-15 11:32:03 +020040#include <common/http.h>
41#include <common/h1.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020042#include <common/htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020043
44#include <types/global.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020045#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010046#include <types/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020047
Gaetan Rivet707b52f2020-02-21 18:14:59 +010048#include <proto/action.h>
Christopher Fauletba3c68f2020-04-01 16:27:05 +020049#include <proto/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020050#include <proto/backend.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020051#include <proto/checks.h>
William Lallemand9ed62032016-11-21 17:49:11 +010052#include <proto/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020053#include <proto/fd.h>
Christopher Faulet14cd3162020-04-16 14:50:06 +020054#include <proto/http_htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020055#include <proto/log.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020056#include <proto/mux_pt.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020057#include <proto/queue.h>
Willy Tarreauc6f4ce82009-06-10 11:09:37 +020058#include <proto/port_range.h>
Willy Tarreaue8c66af2008-01-13 18:40:14 +010059#include <proto/proto_tcp.h>
Baptiste Assmann69e273f2013-12-11 00:52:19 +010060#include <proto/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010061#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020062#include <proto/server.h>
Willy Tarreau48d6bf22016-06-21 16:27:34 +020063#include <proto/signal.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010064#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020065#include <proto/task.h>
Gaetan Rivet13a50432020-02-21 18:13:44 +010066#include <proto/vars.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020067#include <proto/log.h>
68#include <proto/dns.h>
69#include <proto/proto_udp.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020070#include <proto/ssl_sock.h>
Christopher Fauletb7d30092020-03-30 15:19:03 +020071#include <proto/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020072
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020073static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010074
Christopher Faulet61cc8522020-04-20 14:54:42 +020075static int wake_srv_chk(struct conn_stream *cs);
76struct data_cb check_conn_cb = {
77 .wake = wake_srv_chk,
78 .name = "CHCK",
79};
Christopher Fauletd7e63962020-04-17 20:15:59 +020080
Christopher Fauletd7cee712020-04-21 13:45:00 +020081/* Global tree to share all tcp-checks */
82struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020083
84
Willy Tarreau8ceae722018-11-26 11:58:30 +010085DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
86DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020087
Gaetan Rivet05d692d2020-02-14 17:42:54 +010088/* Dummy frontend used to create all checks sessions. */
89static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020090
Christopher Faulet61cc8522020-04-20 14:54:42 +020091/**************************************************************************/
92/************************ Handle check results ****************************/
93/**************************************************************************/
94struct check_status {
95 short result; /* one of SRV_CHK_* */
96 char *info; /* human readable short info */
97 char *desc; /* long description */
98};
99
100struct analyze_status {
101 char *desc; /* description */
102 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
103};
104
Simon Horman63a4a822012-03-19 07:24:41 +0900105static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100106 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
107 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200108 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200109
Willy Tarreau23964182014-05-20 20:56:30 +0200110 /* Below we have finished checks */
111 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100112 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100113
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100114 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200115
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100116 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
117 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
118 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200119
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100120 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
121 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
122 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200123
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100124 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
125 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200126
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200127 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200128
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100129 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
130 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
131 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900132
133 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
134 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200135 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200136};
137
Simon Horman63a4a822012-03-19 07:24:41 +0900138static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100139 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
140
141 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
142 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
143
144 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
145 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
146 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
147 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
148
149 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
150 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
151 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
152};
153
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100154/* checks if <err> is a real error for errno or one that can be ignored, and
155 * return 0 for these ones or <err> for real ones.
156 */
157static inline int unclean_errno(int err)
158{
159 if (err == EAGAIN || err == EINPROGRESS ||
160 err == EISCONN || err == EALREADY)
161 return 0;
162 return err;
163}
164
Christopher Faulet61cc8522020-04-20 14:54:42 +0200165/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200166const char *get_check_status_description(short check_status) {
167
168 const char *desc;
169
170 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200171 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200172 else
173 desc = NULL;
174
175 if (desc && *desc)
176 return desc;
177 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200178 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200179}
180
Christopher Faulet61cc8522020-04-20 14:54:42 +0200181/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200182const char *get_check_status_info(short check_status) {
183
184 const char *info;
185
186 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200187 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200188 else
189 info = NULL;
190
191 if (info && *info)
192 return info;
193 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200194 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200195}
196
Christopher Faulet61cc8522020-04-20 14:54:42 +0200197/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100198const char *get_analyze_status(short analyze_status) {
199
200 const char *desc;
201
202 if (analyze_status < HANA_STATUS_SIZE)
203 desc = analyze_statuses[analyze_status].desc;
204 else
205 desc = NULL;
206
207 if (desc && *desc)
208 return desc;
209 else
210 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
211}
212
Christopher Faulet61cc8522020-04-20 14:54:42 +0200213/* Sets check->status, update check->duration and fill check->result with an
214 * adequate CHK_RES_* value. The new check->health is computed based on the
215 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200216 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200217 * Shows information in logs about failed health check if server is UP or
218 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200219 */
Simon Horman4a741432013-02-23 15:35:38 +0900220static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100221{
Simon Horman4a741432013-02-23 15:35:38 +0900222 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200223 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200224 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900225
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200226 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100227 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900228 check->desc[0] = '\0';
229 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200230 return;
231 }
232
Simon Horman4a741432013-02-23 15:35:38 +0900233 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200234 return;
235
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200236 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900237 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
238 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200239 } else
Simon Horman4a741432013-02-23 15:35:38 +0900240 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200241
Simon Horman4a741432013-02-23 15:35:38 +0900242 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200243 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900244 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200245
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100246 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900247 check->duration = -1;
248 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200249 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900250 check->duration = tv_ms_elapsed(&check->start, &now);
251 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200252 }
253
Willy Tarreau23964182014-05-20 20:56:30 +0200254 /* no change is expected if no state change occurred */
255 if (check->result == CHK_RES_NEUTRAL)
256 return;
257
Olivier Houchard0923fa42019-01-11 18:43:04 +0100258 /* If the check was really just sending a mail, it won't have an
259 * associated server, so we're done now.
260 */
261 if (!s)
262 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200263 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200264
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200265 switch (check->result) {
266 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200267 /* Failure to connect to the agent as a secondary check should not
268 * cause the server to be marked down.
269 */
270 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900271 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200272 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100273 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200274 report = 1;
275 check->health--;
276 if (check->health < check->rise)
277 check->health = 0;
278 }
279 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200280
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200281 case CHK_RES_PASSED:
282 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
283 if ((check->health < check->rise + check->fall - 1) &&
284 (check->result == CHK_RES_PASSED || check->health > 0)) {
285 report = 1;
286 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200287
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200288 if (check->health >= check->rise)
289 check->health = check->rise + check->fall - 1; /* OK now */
290 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200291
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200292 /* clear consecutive_errors if observing is enabled */
293 if (s->onerror)
294 s->consecutive_errors = 0;
295 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100296
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200297 default:
298 break;
299 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200300
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200301 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
302 (status != prev_status || report)) {
303 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200304 "%s check for %sserver %s/%s %s%s",
305 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200306 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100307 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100308 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200309 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200310
Emeric Brun5a133512017-10-19 14:42:30 +0200311 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200312
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100313 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200314 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
315 (check->health >= check->rise) ? check->fall : check->rise,
316 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200317
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200318 ha_warning("%s.\n", trash.area);
319 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
320 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200321 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200322}
323
Willy Tarreau4eec5472014-05-20 22:32:27 +0200324/* Marks the check <check>'s server down if the current check is already failed
325 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200326 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200327static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200328{
Simon Horman4a741432013-02-23 15:35:38 +0900329 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900330
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200331 /* The agent secondary check should only cause a server to be marked
332 * as down if check->status is HCHK_STATUS_L7STS, which indicates
333 * that the agent returned "fail", "stopped" or "down".
334 * The implication here is that failure to connect to the agent
335 * as a secondary check should not cause the server to be marked
336 * down. */
337 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
338 return;
339
Willy Tarreau4eec5472014-05-20 22:32:27 +0200340 if (check->health > 0)
341 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100342
Willy Tarreau4eec5472014-05-20 22:32:27 +0200343 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200344 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200345}
346
Willy Tarreauaf549582014-05-16 17:37:50 +0200347/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200348 * it isn't in maintenance, it is not tracking a down server and other checks
349 * comply. The rule is simple : by default, a server is up, unless any of the
350 * following conditions is true :
351 * - health check failed (check->health < rise)
352 * - agent check failed (agent->health < rise)
353 * - the server tracks a down server (track && track->state == STOPPED)
354 * Note that if the server has a slowstart, it will switch to STARTING instead
355 * of RUNNING. Also, only the health checks support the nolb mode, so the
356 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200357 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200358static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200359{
Simon Horman4a741432013-02-23 15:35:38 +0900360 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100361
Emeric Brun52a91d32017-08-31 14:41:55 +0200362 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200363 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100364
Emeric Brun52a91d32017-08-31 14:41:55 +0200365 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200366 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100367
Willy Tarreau3e048382014-05-21 10:30:54 +0200368 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
369 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100370
Willy Tarreau3e048382014-05-21 10:30:54 +0200371 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
372 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200373
Emeric Brun52a91d32017-08-31 14:41:55 +0200374 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200375 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100376
Emeric Brun5a133512017-10-19 14:42:30 +0200377 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100378}
379
Willy Tarreaudb58b792014-05-21 13:57:23 +0200380/* Marks the check <check> as valid and tries to set its server into stopping mode
381 * if it was running or starting, and provided it isn't in maintenance and other
382 * checks comply. The conditions for the server to be marked in stopping mode are
383 * the same as for it to be turned up. Also, only the health checks support the
384 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200385 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200386static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200387{
Simon Horman4a741432013-02-23 15:35:38 +0900388 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100389
Emeric Brun52a91d32017-08-31 14:41:55 +0200390 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200391 return;
392
Willy Tarreaudb58b792014-05-21 13:57:23 +0200393 if (check->state & CHK_ST_AGENT)
394 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100395
Emeric Brun52a91d32017-08-31 14:41:55 +0200396 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200397 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100398
Willy Tarreaudb58b792014-05-21 13:57:23 +0200399 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
400 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100401
Willy Tarreaudb58b792014-05-21 13:57:23 +0200402 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
403 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100404
Willy Tarreaub26881a2017-12-23 11:16:49 +0100405 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100406}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200407
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100408/* note: use health_adjust() only, which first checks that the observe mode is
409 * enabled.
410 */
411void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100412{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100413 int failed;
414 int expire;
415
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100416 if (s->observe >= HANA_OBS_SIZE)
417 return;
418
Willy Tarreaubb956662013-01-24 00:37:39 +0100419 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100420 return;
421
422 switch (analyze_statuses[status].lr[s->observe - 1]) {
423 case 1:
424 failed = 1;
425 break;
426
427 case 2:
428 failed = 0;
429 break;
430
431 default:
432 return;
433 }
434
435 if (!failed) {
436 /* good: clear consecutive_errors */
437 s->consecutive_errors = 0;
438 return;
439 }
440
Olivier Houchard7059c552019-03-08 18:49:32 +0100441 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100442
443 if (s->consecutive_errors < s->consecutive_errors_limit)
444 return;
445
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100446 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
447 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100448
449 switch (s->onerror) {
450 case HANA_ONERR_FASTINTER:
451 /* force fastinter - nothing to do here as all modes force it */
452 break;
453
454 case HANA_ONERR_SUDDTH:
455 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900456 if (s->check.health > s->check.rise)
457 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100458
459 /* no break - fall through */
460
461 case HANA_ONERR_FAILCHK:
462 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200463 set_server_check_status(&s->check, HCHK_STATUS_HANA,
464 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200465 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100466 break;
467
468 case HANA_ONERR_MARKDWN:
469 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900470 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200471 set_server_check_status(&s->check, HCHK_STATUS_HANA,
472 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200473 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100474 break;
475
476 default:
477 /* write a warning? */
478 break;
479 }
480
481 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100482 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100483
Simon Horman66183002013-02-23 10:16:43 +0900484 if (s->check.fastinter) {
485 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300486 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200487 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300488 /* requeue check task with new expire */
489 task_queue(s->check.task);
490 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100491 }
Willy Tarreauef781042010-01-27 11:53:01 +0100492}
493
Christopher Faulet61cc8522020-04-20 14:54:42 +0200494/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100495 * closed, keep errno intact as it is supposed to contain the valid error code.
496 * If no error is reported, check the socket's error queue using getsockopt().
497 * Warning, this must be done only once when returning from poll, and never
498 * after an I/O error was attempted, otherwise the error queue might contain
499 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
500 * socket. Returns non-zero if an error was reported, zero if everything is
501 * clean (including a properly closed socket).
502 */
503static int retrieve_errno_from_socket(struct connection *conn)
504{
505 int skerr;
506 socklen_t lskerr = sizeof(skerr);
507
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100508 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100509 return 1;
510
Willy Tarreau3c728722014-01-23 13:50:42 +0100511 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100512 return 0;
513
Willy Tarreau585744b2017-08-24 14:31:19 +0200514 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100515 errno = skerr;
516
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100517 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100518
519 if (!errno) {
520 /* we could not retrieve an error, that does not mean there is
521 * none. Just don't change anything and only report the prior
522 * error if any.
523 */
524 if (conn->flags & CO_FL_ERROR)
525 return 1;
526 else
527 return 0;
528 }
529
530 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
531 return 1;
532}
533
Christopher Faulet61cc8522020-04-20 14:54:42 +0200534/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100535 * and adjust the server status accordingly. It may make use of <errno_bck>
536 * if non-null when the caller is absolutely certain of its validity (eg:
537 * checked just after a syscall). If the caller doesn't have a valid errno,
538 * it can pass zero, and retrieve_errno_from_socket() will be called to try
539 * to extract errno from the socket. If no error is reported, it will consider
540 * the <expired> flag. This is intended to be used when a connection error was
541 * reported in conn->flags or when a timeout was reported in <expired>. The
542 * function takes care of not updating a server status which was already set.
543 * All situations where at least one of <expired> or CO_FL_ERROR are set
544 * produce a status.
545 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200546static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100547{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200548 struct conn_stream *cs = check->cs;
549 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100550 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200551 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200552 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100553
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100554 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100555 return;
556
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100557 errno = unclean_errno(errno_bck);
558 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100559 retrieve_errno_from_socket(conn);
560
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200561 if (conn && !(conn->flags & CO_FL_ERROR) &&
562 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100563 return;
564
565 /* we'll try to build a meaningful error message depending on the
566 * context of the error possibly present in conn->err_code, and the
567 * socket error possibly collected above. This is useful to know the
568 * exact step of the L6 layer (eg: SSL handshake).
569 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200570 chk = get_trash_chunk();
571
Christopher Faulet799f3a42020-04-07 12:06:14 +0200572 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200573 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200574 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200575 if (!step)
576 chunk_printf(chk, " at initial connection step of tcp-check");
577 else {
578 chunk_printf(chk, " at step %d of tcp-check", step);
579 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200580 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
581 if (check->current_step->connect.port)
582 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200583 else
584 chunk_appendf(chk, " (connect)");
585 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200586 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
587 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100588
589 switch (expect->type) {
590 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200591 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100592 break;
593 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200594 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100595 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200596 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200597 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100598 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200599 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100600 chunk_appendf(chk, " (expect binary regex)");
601 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200602 case TCPCHK_EXPECT_STRING_LF:
603 chunk_appendf(chk, " (expect log-format string)");
604 break;
605 case TCPCHK_EXPECT_BINARY_LF:
606 chunk_appendf(chk, " (expect log-format binary)");
607 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200608 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200609 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200610 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200611 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200612 chunk_appendf(chk, " (expect HTTP status regex)");
613 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200614 case TCPCHK_EXPECT_HTTP_HEADER:
615 chunk_appendf(chk, " (expect HTTP header pattern)");
616 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200617 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200618 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200619 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200620 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200621 chunk_appendf(chk, " (expect HTTP body regex)");
622 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200623 case TCPCHK_EXPECT_HTTP_BODY_LF:
624 chunk_appendf(chk, " (expect log-format HTTP body)");
625 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200626 case TCPCHK_EXPECT_CUSTOM:
627 chunk_appendf(chk, " (expect custom function)");
628 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100629 case TCPCHK_EXPECT_UNDEF:
630 chunk_appendf(chk, " (undefined expect!)");
631 break;
632 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200633 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200634 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200635 chunk_appendf(chk, " (send)");
636 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200637
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200638 if (check->current_step && check->current_step->comment)
639 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200640 }
641 }
642
Willy Tarreau00149122017-10-04 18:05:01 +0200643 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100644 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200645 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
646 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100647 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200648 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
649 chk->area);
650 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100651 }
652 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100653 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200654 chunk_printf(&trash, "%s%s", strerror(errno),
655 chk->area);
656 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100657 }
658 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200659 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100660 }
661 }
662
Willy Tarreau00149122017-10-04 18:05:01 +0200663 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200664 /* NOTE: this is reported after <fall> tries */
665 chunk_printf(chk, "No port available for the TCP connection");
666 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
667 }
668
Willy Tarreau00149122017-10-04 18:05:01 +0200669 if (!conn) {
670 /* connection allocation error before the connection was established */
671 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
672 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100673 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100674 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200675 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100676 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
677 else if (expired)
678 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200679
680 /*
681 * might be due to a server IP change.
682 * Let's trigger a DNS resolution if none are currently running.
683 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100684 if (check->server)
685 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200686
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100687 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100688 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100689 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200690 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100691 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
692 else if (expired)
693 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
694 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200695 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100696 /* I/O error after connection was established and before we could diagnose */
697 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
698 }
699 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200700 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
701
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100702 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200703 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
704 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200705 tout = check->current_step->expect.tout_status;
706 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100707 }
708
709 return;
710}
711
Willy Tarreaubaaee002006-06-26 02:48:02 +0200712
Christopher Faulet61cc8522020-04-20 14:54:42 +0200713/**************************************************************************/
714/*************** Init/deinit tcp-check rules and ruleset ******************/
715/**************************************************************************/
716/* Releases memory allocated for a log-format string */
717static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200718{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200719 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100720
Christopher Faulet61cc8522020-04-20 14:54:42 +0200721 list_for_each_entry_safe(lf, lfb, fmt, list) {
722 LIST_DEL(&lf->list);
723 release_sample_expr(lf->expr);
724 free(lf->arg);
725 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100726 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200727}
728
Christopher Faulet61cc8522020-04-20 14:54:42 +0200729/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
730static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100731{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200732 if (!hdr)
733 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200734
Christopher Faulet61cc8522020-04-20 14:54:42 +0200735 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200736 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200737 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100738}
739
Christopher Faulet61cc8522020-04-20 14:54:42 +0200740/* Releases memory allocated for an HTTP header list used in a tcp-check send
741 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200742 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200743static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200744{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200745 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200746
Christopher Faulet61cc8522020-04-20 14:54:42 +0200747 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
748 LIST_DEL(&hdr->list);
749 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200750 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200751}
752
Christopher Faulet61cc8522020-04-20 14:54:42 +0200753/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
754 * tcp-check was allocated using a memory pool (it is used to instantiate email
755 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200756 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200757static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200758{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200759 if (!rule)
760 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200761
Christopher Faulet61cc8522020-04-20 14:54:42 +0200762 free(rule->comment);
763 switch (rule->action) {
764 case TCPCHK_ACT_SEND:
765 switch (rule->send.type) {
766 case TCPCHK_SEND_STRING:
767 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200768 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200769 break;
770 case TCPCHK_SEND_STRING_LF:
771 case TCPCHK_SEND_BINARY_LF:
772 free_tcpcheck_fmt(&rule->send.fmt);
773 break;
774 case TCPCHK_SEND_HTTP:
775 free(rule->send.http.meth.str.area);
776 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200777 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200778 else
779 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200780 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200781 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
782 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200783 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200784 else
785 free_tcpcheck_fmt(&rule->send.http.body_fmt);
786 break;
787 case TCPCHK_SEND_UNDEF:
788 break;
789 }
790 break;
791 case TCPCHK_ACT_EXPECT:
792 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
793 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
794 release_sample_expr(rule->expect.status_expr);
795 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200796 case TCPCHK_EXPECT_HTTP_STATUS:
797 free(rule->expect.codes.codes);
798 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200799 case TCPCHK_EXPECT_STRING:
800 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200801 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200802 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200803 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200804 case TCPCHK_EXPECT_STRING_REGEX:
805 case TCPCHK_EXPECT_BINARY_REGEX:
806 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
807 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200808 regex_free(rule->expect.regex);
809 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200810 case TCPCHK_EXPECT_STRING_LF:
811 case TCPCHK_EXPECT_BINARY_LF:
812 case TCPCHK_EXPECT_HTTP_BODY_LF:
813 free_tcpcheck_fmt(&rule->expect.fmt);
814 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200815 case TCPCHK_EXPECT_HTTP_HEADER:
816 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
817 regex_free(rule->expect.hdr.name_re);
818 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
819 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
820 else
821 istfree(&rule->expect.hdr.name);
822
823 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
824 regex_free(rule->expect.hdr.value_re);
825 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
826 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
827 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
828 istfree(&rule->expect.hdr.value);
829 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200830 case TCPCHK_EXPECT_CUSTOM:
831 case TCPCHK_EXPECT_UNDEF:
832 break;
833 }
834 break;
835 case TCPCHK_ACT_CONNECT:
836 free(rule->connect.sni);
837 free(rule->connect.alpn);
838 release_sample_expr(rule->connect.port_expr);
839 break;
840 case TCPCHK_ACT_COMMENT:
841 break;
842 case TCPCHK_ACT_ACTION_KW:
843 free(rule->action_kw.rule);
844 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200845 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200846
847 if (in_pool)
848 pool_free(pool_head_tcpcheck_rule, rule);
849 else
850 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200851}
852
Christopher Faulet61cc8522020-04-20 14:54:42 +0200853/* Creates a tcp-check variable used in preset variables before executing a
854 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100855 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200856static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100857{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200858 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100859
Christopher Faulet61cc8522020-04-20 14:54:42 +0200860 var = calloc(1, sizeof(*var));
861 if (var == NULL)
862 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100863
Christopher Fauletb61caf42020-04-21 10:57:42 +0200864 var->name = istdup(name);
865 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200866 free(var);
867 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100868 }
Simon Horman98637e52014-06-20 12:30:16 +0900869
Christopher Faulet61cc8522020-04-20 14:54:42 +0200870 LIST_INIT(&var->list);
871 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900872}
873
Christopher Faulet61cc8522020-04-20 14:54:42 +0200874/* Releases memory allocated for a preset tcp-check variable */
875static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900876{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200877 if (!var)
878 return;
879
Christopher Fauletb61caf42020-04-21 10:57:42 +0200880 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200881 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
882 free(var->data.u.str.area);
883 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
884 free(var->data.u.meth.str.area);
885 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900886}
887
Christopher Faulet61cc8522020-04-20 14:54:42 +0200888/* Releases a list of preset tcp-check variables */
889static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900890{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200891 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200892
Christopher Faulet61cc8522020-04-20 14:54:42 +0200893 list_for_each_entry_safe(var, back, vars, list) {
894 LIST_DEL(&var->list);
895 free_tcpcheck_var(var);
896 }
Simon Horman98637e52014-06-20 12:30:16 +0900897}
898
Christopher Faulet61cc8522020-04-20 14:54:42 +0200899/* Duplicate a list of preset tcp-check variables */
900int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900901{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200902 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900903
Christopher Faulet61cc8522020-04-20 14:54:42 +0200904 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200905 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200906 if (!new)
907 goto error;
908 new->data.type = var->data.type;
909 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
910 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
911 goto error;
912 if (var->data.type == SMP_T_STR)
913 new->data.u.str.area[new->data.u.str.data] = 0;
914 }
915 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
916 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
917 goto error;
918 new->data.u.str.area[new->data.u.str.data] = 0;
919 new->data.u.meth.meth = var->data.u.meth.meth;
920 }
921 else
922 new->data.u = var->data.u;
923 LIST_ADDQ(dst, &new->list);
924 }
925 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900926
Christopher Faulet61cc8522020-04-20 14:54:42 +0200927 error:
928 free(new);
929 return 0;
930}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200931
Christopher Faulet61cc8522020-04-20 14:54:42 +0200932/* Looks for a shared tcp-check ruleset given its name. */
933static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
934{
935 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200936 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900937
Christopher Fauletd7cee712020-04-21 13:45:00 +0200938 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
939 if (node) {
940 rs = container_of(node, typeof(*rs), node);
941 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200942 }
943 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900944}
945
Christopher Faulet56192cc2020-05-29 08:10:50 +0200946/* Creates a new shared tcp-check ruleset and insert it in shared_tcpchecks
947 * tree.
948 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200949static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900950{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200951 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900952
Christopher Faulet61cc8522020-04-20 14:54:42 +0200953 rs = calloc(1, sizeof(*rs));
954 if (rs == NULL)
955 return NULL;
956
Christopher Fauletd7cee712020-04-21 13:45:00 +0200957 rs->node.key = strdup(name);
958 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200959 free(rs);
960 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900961 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962
Christopher Faulet61cc8522020-04-20 14:54:42 +0200963 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200964 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200965 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900966}
967
Christopher Faulet61cc8522020-04-20 14:54:42 +0200968/* Releases memory allocated by a tcp-check ruleset. */
969static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900970{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200971 struct tcpcheck_rule *r, *rb;
972 if (!rs)
973 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200974
Christopher Fauletd7cee712020-04-21 13:45:00 +0200975 ebpt_delete(&rs->node);
976 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200977 list_for_each_entry_safe(r, rb, &rs->rules, list) {
978 LIST_DEL(&r->list);
979 free_tcpcheck(r, 0);
980 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200981 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900982}
983
Christopher Faulet61cc8522020-04-20 14:54:42 +0200984
985/**************************************************************************/
986/**************** Everything about tcp-checks execution *******************/
987/**************************************************************************/
988/* Returns the id of a step in a tcp-check ruleset */
989static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200990{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200991 if (!rule)
992 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900993
Christopher Faulet61cc8522020-04-20 14:54:42 +0200994 /* no last started step => first step */
995 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900996 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900997
Christopher Faulet61cc8522020-04-20 14:54:42 +0200998 /* last step is the first implicit connect */
999 if (rule->index == 0 &&
1000 rule->action == TCPCHK_ACT_CONNECT &&
1001 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1002 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001003
Christopher Faulet61cc8522020-04-20 14:54:42 +02001004 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001005}
1006
Christopher Faulet61cc8522020-04-20 14:54:42 +02001007/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1008 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001009 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001010static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001011{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001012 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001013
Christopher Faulet61cc8522020-04-20 14:54:42 +02001014 list_for_each_entry(r, rules->list, list) {
1015 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1016 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001017 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001018 return NULL;
1019}
Cyril Bontéac92a062014-12-27 22:28:38 +01001020
Christopher Faulet61cc8522020-04-20 14:54:42 +02001021/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1022 * NULL if none was found.
1023 */
1024static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1025{
1026 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001027
Christopher Faulet61cc8522020-04-20 14:54:42 +02001028 list_for_each_entry_rev(r, rules->list, list) {
1029 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1030 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001031 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001032 return NULL;
1033}
Cyril Bontéac92a062014-12-27 22:28:38 +01001034
Christopher Faulet61cc8522020-04-20 14:54:42 +02001035/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1036 * <start> or NULL if non was found. If <start> is NULL, it relies on
1037 * get_first_tcpcheck_rule().
1038 */
1039static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1040{
1041 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001042
Christopher Faulet61cc8522020-04-20 14:54:42 +02001043 if (!start)
1044 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001045
Christopher Faulet61cc8522020-04-20 14:54:42 +02001046 r = LIST_NEXT(&start->list, typeof(r), list);
1047 list_for_each_entry_from(r, rules->list, list) {
1048 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1049 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001050 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001051 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001052}
Simon Horman98637e52014-06-20 12:30:16 +09001053
Simon Horman98637e52014-06-20 12:30:16 +09001054
Christopher Faulet61cc8522020-04-20 14:54:42 +02001055/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1056static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1057 int match, struct ist info)
1058{
1059 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001060
Christopher Faulet61cc8522020-04-20 14:54:42 +02001061 /* Follows these step to produce the info message:
1062 * 1. if info field is already provided, copy it
1063 * 2. if the expect rule provides an onerror log-format string,
1064 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001065 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001066 * 4. Otherwise produce the generic tcp-check info message
1067 */
1068 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001069 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001070 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001071 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001072 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1073 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1074 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001075 }
Simon Horman98637e52014-06-20 12:30:16 +09001076
Christopher Faulet61cc8522020-04-20 14:54:42 +02001077 if (check->type == PR_O2_TCPCHK_CHK &&
1078 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1079 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001080
Christopher Faulet61cc8522020-04-20 14:54:42 +02001081 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1082 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001083 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001084 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1085 break;
1086 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001087 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001088 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001089 tcpcheck_get_step_id(check, rule));
1090 break;
1091 case TCPCHK_EXPECT_BINARY:
1092 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1093 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001094 case TCPCHK_EXPECT_STRING_REGEX:
1095 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1096 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001097 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1098 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001099 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001100 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001101 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001102 case TCPCHK_EXPECT_STRING_LF:
1103 case TCPCHK_EXPECT_HTTP_BODY_LF:
1104 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1105 break;
1106 case TCPCHK_EXPECT_BINARY_LF:
1107 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1108 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001109 case TCPCHK_EXPECT_CUSTOM:
1110 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1111 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001112 case TCPCHK_EXPECT_HTTP_HEADER:
1113 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001114 case TCPCHK_EXPECT_UNDEF:
1115 /* Should never happen. */
1116 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001117 }
1118
Christopher Faulet61cc8522020-04-20 14:54:42 +02001119 comment:
1120 /* If the failing expect rule provides a comment, it is concatenated to
1121 * the info message.
1122 */
1123 if (rule->comment) {
1124 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001125 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001126 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001127
Christopher Faulet61cc8522020-04-20 14:54:42 +02001128 /* Finally, the check status code is set if the failing expect rule
1129 * defines a status expression.
1130 */
1131 if (rule->expect.status_expr) {
1132 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001133 rule->expect.status_expr, SMP_T_STR);
1134
1135 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1136 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001137 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001138 }
Simon Horman98637e52014-06-20 12:30:16 +09001139
Christopher Faulet61cc8522020-04-20 14:54:42 +02001140 *(b_tail(msg)) = '\0';
1141}
Cyril Bontéac92a062014-12-27 22:28:38 +01001142
Christopher Faulet61cc8522020-04-20 14:54:42 +02001143/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1144static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1145 struct ist info)
1146{
1147 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001148
Christopher Faulet61cc8522020-04-20 14:54:42 +02001149 /* Follows these step to produce the info message:
1150 * 1. if info field is already provided, copy it
1151 * 2. if the expect rule provides an onsucces log-format string,
1152 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001153 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001154 * 4. Otherwise produce the generic tcp-check info message
1155 */
1156 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001157 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001158 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1159 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1160 &rule->expect.onsuccess_fmt);
1161 else if (check->type == PR_O2_TCPCHK_CHK &&
1162 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1163 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001164
Christopher Faulet61cc8522020-04-20 14:54:42 +02001165 /* Finally, the check status code is set if the expect rule defines a
1166 * status expression.
1167 */
1168 if (rule->expect.status_expr) {
1169 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001170 rule->expect.status_expr, SMP_T_STR);
1171
1172 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1173 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001174 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001175 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001176
1177 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001178}
1179
Christopher Faulet61cc8522020-04-20 14:54:42 +02001180/* Builds the server state header used by HTTP health-checks */
1181static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001182{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001183 int sv_state;
1184 int ratio;
1185 char addr[46];
1186 char port[6];
1187 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1188 "UP %d/%d", "UP",
1189 "NOLB %d/%d", "NOLB",
1190 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001191
Christopher Faulet61cc8522020-04-20 14:54:42 +02001192 if (!(s->check.state & CHK_ST_ENABLED))
1193 sv_state = 6;
1194 else if (s->cur_state != SRV_ST_STOPPED) {
1195 if (s->check.health == s->check.rise + s->check.fall - 1)
1196 sv_state = 3; /* UP */
1197 else
1198 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001199
Christopher Faulet61cc8522020-04-20 14:54:42 +02001200 if (s->cur_state == SRV_ST_STOPPING)
1201 sv_state += 2;
1202 } else {
1203 if (s->check.health)
1204 sv_state = 1; /* going up */
1205 else
1206 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001207 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001208
Christopher Faulet61cc8522020-04-20 14:54:42 +02001209 chunk_appendf(buf, srv_hlt_st[sv_state],
1210 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1211 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001212
Christopher Faulet61cc8522020-04-20 14:54:42 +02001213 addr_to_str(&s->addr, addr, sizeof(addr));
1214 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1215 snprintf(port, sizeof(port), "%u", s->svc_port);
1216 else
1217 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001218
Christopher Faulet61cc8522020-04-20 14:54:42 +02001219 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1220 addr, port, s->proxy->id, s->id,
1221 global.node,
1222 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1223 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1224 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1225 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001226
Christopher Faulet61cc8522020-04-20 14:54:42 +02001227 if ((s->cur_state == SRV_ST_STARTING) &&
1228 now.tv_sec < s->last_change + s->slowstart &&
1229 now.tv_sec >= s->last_change) {
1230 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1231 chunk_appendf(buf, "; throttle=%d%%", ratio);
1232 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001233
Christopher Faulet61cc8522020-04-20 14:54:42 +02001234 return b_data(buf);
1235}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001236
Christopher Faulet61cc8522020-04-20 14:54:42 +02001237/* Internal functions to parse and validate a MySQL packet in the context of an
1238 * expect rule. It start to parse the input buffer at the offset <offset>. If
1239 * <last_read> is set, no more data are expected.
1240 */
1241static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1242 unsigned int offset, int last_read)
1243{
1244 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1245 enum healthcheck_status status;
1246 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001247 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001248 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001249
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001250
Christopher Faulet61cc8522020-04-20 14:54:42 +02001251 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001252 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001253 if (!last_read)
1254 goto wait_more_data;
1255
1256 /* invalid length or truncated response */
1257 status = HCHK_STATUS_L7RSP;
1258 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001259 }
1260
Christopher Faulet61cc8522020-04-20 14:54:42 +02001261 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1262 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1263 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001264
Christopher Faulet61cc8522020-04-20 14:54:42 +02001265 if (b_data(&check->bi) < offset+plen+4) {
1266 if (!last_read)
1267 goto wait_more_data;
1268
1269 /* invalid length or truncated response */
1270 status = HCHK_STATUS_L7RSP;
1271 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001272 }
Simon Horman98637e52014-06-20 12:30:16 +09001273
Christopher Faulet61cc8522020-04-20 14:54:42 +02001274 if (*b_peek(&check->bi, offset+4) == '\xff') {
1275 /* MySQL Error packet always begin with field_count = 0xff */
1276 status = HCHK_STATUS_L7STS;
1277 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1278 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1279 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1280 goto error;
1281 }
Simon Horman98637e52014-06-20 12:30:16 +09001282
Christopher Faulet61cc8522020-04-20 14:54:42 +02001283 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1284 /* Not the last rule, continue */
1285 goto out;
1286 }
Simon Horman98637e52014-06-20 12:30:16 +09001287
Christopher Faulet61cc8522020-04-20 14:54:42 +02001288 /* We set the MySQL Version in description for information purpose
1289 * FIXME : it can be cool to use MySQL Version for other purpose,
1290 * like mark as down old MySQL server.
1291 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001292 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1293 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001294
Christopher Faulet61cc8522020-04-20 14:54:42 +02001295 out:
1296 free_trash_chunk(msg);
1297 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001298
Christopher Faulet61cc8522020-04-20 14:54:42 +02001299 error:
1300 ret = TCPCHK_EVAL_STOP;
1301 check->code = err;
1302 msg = alloc_trash_chunk();
1303 if (msg)
1304 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1305 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1306 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001307
Christopher Faulet61cc8522020-04-20 14:54:42 +02001308 wait_more_data:
1309 ret = TCPCHK_EVAL_WAIT;
1310 goto out;
1311}
Simon Horman98637e52014-06-20 12:30:16 +09001312
Christopher Faulet61cc8522020-04-20 14:54:42 +02001313/* Custom tcp-check expect function to parse and validate the MySQL initial
1314 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1315 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1316 * error occurred.
1317 */
1318static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1319{
1320 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1321}
Simon Horman98637e52014-06-20 12:30:16 +09001322
Christopher Faulet61cc8522020-04-20 14:54:42 +02001323/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1324 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1325 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1326 * an error occurred.
1327 */
1328static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1329{
1330 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001331
Christopher Faulet61cc8522020-04-20 14:54:42 +02001332 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1333 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1334 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001335
Christopher Faulet61cc8522020-04-20 14:54:42 +02001336 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1337}
Simon Horman98637e52014-06-20 12:30:16 +09001338
Christopher Faulet61cc8522020-04-20 14:54:42 +02001339/* Custom tcp-check expect function to parse and validate the LDAP bind response
1340 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1341 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1342 * error occurred.
1343 */
1344static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1345{
1346 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1347 enum healthcheck_status status;
1348 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001349 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001350 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001351
Christopher Faulet61cc8522020-04-20 14:54:42 +02001352 /* Check if the server speaks LDAP (ASN.1/BER)
1353 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1354 * http://tools.ietf.org/html/rfc4511
1355 */
1356 /* size of LDAPMessage */
1357 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001358
Christopher Faulet61cc8522020-04-20 14:54:42 +02001359 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1360 * messageID: 0x02 0x01 0x01: INTEGER 1
1361 * protocolOp: 0x61: bindResponse
1362 */
1363 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1364 status = HCHK_STATUS_L7RSP;
1365 desc = ist("Not LDAPv3 protocol");
1366 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001367 }
Simon Horman98637e52014-06-20 12:30:16 +09001368
Christopher Faulet61cc8522020-04-20 14:54:42 +02001369 /* size of bindResponse */
1370 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001371
Christopher Faulet61cc8522020-04-20 14:54:42 +02001372 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1373 * ldapResult: 0x0a 0x01: ENUMERATION
1374 */
1375 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1376 status = HCHK_STATUS_L7RSP;
1377 desc = ist("Not LDAPv3 protocol");
1378 goto error;
1379 }
Simon Horman98637e52014-06-20 12:30:16 +09001380
Christopher Faulet61cc8522020-04-20 14:54:42 +02001381 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1382 * resultCode
1383 */
1384 check->code = *(b_head(&check->bi) + msglen + 9);
1385 if (check->code) {
1386 status = HCHK_STATUS_L7STS;
1387 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1388 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001389 }
1390
Christopher Faulet1941bab2020-05-05 07:55:50 +02001391 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1392 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001393
Christopher Faulet61cc8522020-04-20 14:54:42 +02001394 out:
1395 free_trash_chunk(msg);
1396 return ret;
1397
1398 error:
1399 ret = TCPCHK_EVAL_STOP;
1400 msg = alloc_trash_chunk();
1401 if (msg)
1402 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1403 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1404 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001405}
1406
Christopher Faulet61cc8522020-04-20 14:54:42 +02001407/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1408 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1409 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001410 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001411static 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 +02001412{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001413 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1414 enum healthcheck_status status;
1415 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001416 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001417 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001418
Willy Tarreaubaaee002006-06-26 02:48:02 +02001419
Christopher Faulet61cc8522020-04-20 14:54:42 +02001420 memcpy(&framesz, b_head(&check->bi), 4);
1421 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001422
Christopher Faulet61cc8522020-04-20 14:54:42 +02001423 if (!last_read && b_data(&check->bi) < (4+framesz))
1424 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001425
Christopher Faulet61cc8522020-04-20 14:54:42 +02001426 memset(b_orig(&trash), 0, b_size(&trash));
1427 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1428 status = HCHK_STATUS_L7RSP;
1429 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1430 goto error;
1431 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001432
Christopher Faulet1941bab2020-05-05 07:55:50 +02001433 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1434 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001435
Christopher Faulet61cc8522020-04-20 14:54:42 +02001436 out:
1437 free_trash_chunk(msg);
1438 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001439
Christopher Faulet61cc8522020-04-20 14:54:42 +02001440 error:
1441 ret = TCPCHK_EVAL_STOP;
1442 msg = alloc_trash_chunk();
1443 if (msg)
1444 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1445 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1446 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001447
Christopher Faulet61cc8522020-04-20 14:54:42 +02001448 wait_more_data:
1449 ret = TCPCHK_EVAL_WAIT;
1450 goto out;
1451}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001452
Christopher Faulet61cc8522020-04-20 14:54:42 +02001453/* Custom tcp-check expect function to parse and validate the agent-check
1454 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1455 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1456 */
1457static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1458{
1459 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1460 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1461 const char *hs = NULL; /* health status */
1462 const char *as = NULL; /* admin status */
1463 const char *ps = NULL; /* performance status */
1464 const char *cs = NULL; /* maxconn */
1465 const char *err = NULL; /* first error to report */
1466 const char *wrn = NULL; /* first warning to report */
1467 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001468
Christopher Faulet61cc8522020-04-20 14:54:42 +02001469 /* We're getting an agent check response. The agent could
1470 * have been disabled in the mean time with a long check
1471 * still pending. It is important that we ignore the whole
1472 * response.
1473 */
1474 if (!(check->state & CHK_ST_ENABLED))
1475 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001476
Christopher Faulet61cc8522020-04-20 14:54:42 +02001477 /* The agent supports strings made of a single line ended by the
1478 * first CR ('\r') or LF ('\n'). This line is composed of words
1479 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1480 * line may optionally contained a description of a state change
1481 * after a sharp ('#'), which is only considered if a health state
1482 * is announced.
1483 *
1484 * Words may be composed of :
1485 * - a numeric weight suffixed by the percent character ('%').
1486 * - a health status among "up", "down", "stopped", and "fail".
1487 * - an admin status among "ready", "drain", "maint".
1488 *
1489 * These words may appear in any order. If multiple words of the
1490 * same category appear, the last one wins.
1491 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001492
Christopher Faulet61cc8522020-04-20 14:54:42 +02001493 p = b_head(&check->bi);
1494 while (*p && *p != '\n' && *p != '\r')
1495 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001496
Christopher Faulet61cc8522020-04-20 14:54:42 +02001497 if (!*p) {
1498 if (!last_read)
1499 goto wait_more_data;
1500
1501 /* at least inform the admin that the agent is mis-behaving */
1502 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1503 goto out;
1504 }
1505
1506 *p = 0;
1507 cmd = b_head(&check->bi);
1508
1509 while (*cmd) {
1510 /* look for next word */
1511 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1512 cmd++;
1513 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001514 }
1515
Christopher Faulet61cc8522020-04-20 14:54:42 +02001516 if (*cmd == '#') {
1517 /* this is the beginning of a health status description,
1518 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001519 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001520 cmd++;
1521 while (*cmd == '\t' || *cmd == ' ')
1522 cmd++;
1523 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001524 }
1525
Christopher Faulet61cc8522020-04-20 14:54:42 +02001526 /* find the end of the word so that we have a null-terminated
1527 * word between <cmd> and <p>.
1528 */
1529 p = cmd + 1;
1530 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1531 p++;
1532 if (*p)
1533 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001534
Christopher Faulet61cc8522020-04-20 14:54:42 +02001535 /* first, health statuses */
1536 if (strcasecmp(cmd, "up") == 0) {
1537 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1538 status = HCHK_STATUS_L7OKD;
1539 hs = cmd;
1540 }
1541 else if (strcasecmp(cmd, "down") == 0) {
1542 check->server->check.health = 0;
1543 status = HCHK_STATUS_L7STS;
1544 hs = cmd;
1545 }
1546 else if (strcasecmp(cmd, "stopped") == 0) {
1547 check->server->check.health = 0;
1548 status = HCHK_STATUS_L7STS;
1549 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001550 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001551 else if (strcasecmp(cmd, "fail") == 0) {
1552 check->server->check.health = 0;
1553 status = HCHK_STATUS_L7STS;
1554 hs = cmd;
1555 }
1556 /* admin statuses */
1557 else if (strcasecmp(cmd, "ready") == 0) {
1558 as = cmd;
1559 }
1560 else if (strcasecmp(cmd, "drain") == 0) {
1561 as = cmd;
1562 }
1563 else if (strcasecmp(cmd, "maint") == 0) {
1564 as = cmd;
1565 }
1566 /* try to parse a weight here and keep the last one */
1567 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1568 ps = cmd;
1569 }
1570 /* try to parse a maxconn here */
1571 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1572 cs = cmd;
1573 }
1574 else {
1575 /* keep a copy of the first error */
1576 if (!err)
1577 err = cmd;
1578 }
1579 /* skip to next word */
1580 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001581 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001582 /* here, cmd points either to \0 or to the beginning of a
1583 * description. Skip possible leading spaces.
1584 */
1585 while (*cmd == ' ' || *cmd == '\n')
1586 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001587
Christopher Faulet61cc8522020-04-20 14:54:42 +02001588 /* First, update the admin status so that we avoid sending other
1589 * possibly useless warnings and can also update the health if
1590 * present after going back up.
1591 */
1592 if (as) {
1593 if (strcasecmp(as, "drain") == 0)
1594 srv_adm_set_drain(check->server);
1595 else if (strcasecmp(as, "maint") == 0)
1596 srv_adm_set_maint(check->server);
1597 else
1598 srv_adm_set_ready(check->server);
1599 }
Simon Horman98637e52014-06-20 12:30:16 +09001600
Christopher Faulet61cc8522020-04-20 14:54:42 +02001601 /* now change weights */
1602 if (ps) {
1603 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001604
Christopher Faulet61cc8522020-04-20 14:54:42 +02001605 msg = server_parse_weight_change_request(check->server, ps);
1606 if (!wrn || !*wrn)
1607 wrn = msg;
1608 }
Simon Horman98637e52014-06-20 12:30:16 +09001609
Christopher Faulet61cc8522020-04-20 14:54:42 +02001610 if (cs) {
1611 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001612
Christopher Faulet61cc8522020-04-20 14:54:42 +02001613 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001614
Christopher Faulet61cc8522020-04-20 14:54:42 +02001615 msg = server_parse_maxconn_change_request(check->server, cs);
1616 if (!wrn || !*wrn)
1617 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001618 }
1619
Christopher Faulet61cc8522020-04-20 14:54:42 +02001620 /* and finally health status */
1621 if (hs) {
1622 /* We'll report some of the warnings and errors we have
1623 * here. Down reports are critical, we leave them untouched.
1624 * Lack of report, or report of 'UP' leaves the room for
1625 * ERR first, then WARN.
1626 */
1627 const char *msg = cmd;
1628 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001629
Christopher Faulet61cc8522020-04-20 14:54:42 +02001630 if (!*msg || status == HCHK_STATUS_L7OKD) {
1631 if (err && *err)
1632 msg = err;
1633 else if (wrn && *wrn)
1634 msg = wrn;
1635 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001636
Christopher Faulet61cc8522020-04-20 14:54:42 +02001637 t = get_trash_chunk();
1638 chunk_printf(t, "via agent : %s%s%s%s",
1639 hs, *msg ? " (" : "",
1640 msg, *msg ? ")" : "");
1641 set_server_check_status(check, status, t->area);
1642 }
1643 else if (err && *err) {
1644 /* No status change but we'd like to report something odd.
1645 * Just report the current state and copy the message.
1646 */
1647 chunk_printf(&trash, "agent reports an error : %s", err);
1648 set_server_check_status(check, status/*check->status*/, trash.area);
1649 }
1650 else if (wrn && *wrn) {
1651 /* No status change but we'd like to report something odd.
1652 * Just report the current state and copy the message.
1653 */
1654 chunk_printf(&trash, "agent warns : %s", wrn);
1655 set_server_check_status(check, status/*check->status*/, trash.area);
1656 }
1657 else
1658 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001659
Christopher Faulet61cc8522020-04-20 14:54:42 +02001660 out:
1661 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001662
Christopher Faulet61cc8522020-04-20 14:54:42 +02001663 wait_more_data:
1664 ret = TCPCHK_EVAL_WAIT;
1665 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001666}
1667
Christopher Faulet61cc8522020-04-20 14:54:42 +02001668/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1669 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1670 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001671 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001672static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001673{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001674 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1675 struct tcpcheck_connect *connect = &rule->connect;
1676 struct proxy *proxy = check->proxy;
1677 struct server *s = check->server;
1678 struct task *t = check->task;
1679 struct conn_stream *cs;
1680 struct connection *conn = NULL;
1681 struct protocol *proto;
1682 struct xprt_ops *xprt;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001683 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001684 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001685
Christopher Faulet61cc8522020-04-20 14:54:42 +02001686 /* For a connect action we'll create a new connection. We may also have
1687 * to kill a previous one. But we don't want to leave *without* a
1688 * connection if we came here from the connection layer, hence with a
1689 * connection. Thus we'll proceed in the following order :
1690 * 1: close but not release previous connection (handled by the caller)
1691 * 2: try to get a new connection
1692 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001693 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001694
Christopher Faulet61cc8522020-04-20 14:54:42 +02001695 /* 2- prepare new connection */
1696 cs = cs_new(NULL);
1697 if (!cs) {
1698 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1699 tcpcheck_get_step_id(check, rule));
1700 if (rule->comment)
1701 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1702 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1703 ret = TCPCHK_EVAL_STOP;
1704 goto out;
1705 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001706
Christopher Faulet61cc8522020-04-20 14:54:42 +02001707 /* 3- release and replace the old one on success */
1708 if (check->cs) {
1709 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001710 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1711 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001712
1713 /* We may have been scheduled to run, and the I/O handler
1714 * expects to have a cs, so remove the tasklet
1715 */
1716 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1717 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001718 }
1719
Christopher Faulet61cc8522020-04-20 14:54:42 +02001720 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001721
Christopher Faulet61cc8522020-04-20 14:54:42 +02001722 check->cs = cs;
1723 conn = cs->conn;
1724 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001725
Christopher Faulet61cc8522020-04-20 14:54:42 +02001726 /* Maybe there were an older connection we were waiting on */
1727 check->wait_list.events = 0;
1728 conn->target = s ? &s->obj_type : &proxy->obj_type;
1729
1730 /* no client address */
1731 if (!sockaddr_alloc(&conn->dst)) {
1732 status = SF_ERR_RESOURCE;
1733 goto fail_check;
1734 }
1735
1736 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001737 * addr if specified on the server. otherwise, use the server addr (it
1738 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001739 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001740 *conn->dst = (is_addr(&connect->addr)
1741 ? connect->addr
1742 : (is_addr(&check->addr) ? check->addr : s->addr));
1743 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001744
Christopher Faulet61cc8522020-04-20 14:54:42 +02001745 port = 0;
1746 if (!port && connect->port)
1747 port = connect->port;
1748 if (!port && connect->port_expr) {
1749 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001750
Christopher Faulet61cc8522020-04-20 14:54:42 +02001751 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1752 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1753 connect->port_expr, SMP_T_SINT);
1754 if (smp)
1755 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001756 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001757 if (!port && is_inet_addr(&connect->addr))
1758 port = get_host_port(&connect->addr);
1759 if (!port && check->port)
1760 port = check->port;
1761 if (!port && is_inet_addr(&check->addr))
1762 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001763 if (!port) {
1764 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001765 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001766 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001767 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001768
Christopher Faulet61cc8522020-04-20 14:54:42 +02001769 xprt = ((connect->options & TCPCHK_OPT_SSL)
1770 ? xprt_get(XPRT_SSL)
1771 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001772
Christopher Faulet61cc8522020-04-20 14:54:42 +02001773 conn_prepare(conn, proto, xprt);
1774 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001775
Christopher Faulet61cc8522020-04-20 14:54:42 +02001776 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001777 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001778 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001779 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001780
Christopher Faulet61cc8522020-04-20 14:54:42 +02001781 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1782 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001783 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) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001895 if (conn->mux) {
1896 if (next && next->action == TCPCHK_ACT_SEND)
1897 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1898 else
1899 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1900 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001901 ret = TCPCHK_EVAL_WAIT;
1902 goto out;
1903 }
1904
1905 out:
1906 if (conn && check->result == CHK_RES_FAILED)
1907 conn->flags |= CO_FL_ERROR;
1908 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001909}
1910
Christopher Faulet61cc8522020-04-20 14:54:42 +02001911/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1912 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1913 * TCPCHK_EVAL_STOP if an error occurred.
1914 */
1915static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001916{
1917 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001918 struct tcpcheck_send *send = &rule->send;
1919 struct conn_stream *cs = check->cs;
1920 struct connection *conn = cs_conn(cs);
1921 struct buffer *tmp = NULL;
1922 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001923
Christopher Faulet61cc8522020-04-20 14:54:42 +02001924 /* reset the read & write buffer */
1925 b_reset(&check->bi);
1926 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001927
Christopher Faulet61cc8522020-04-20 14:54:42 +02001928 switch (send->type) {
1929 case TCPCHK_SEND_STRING:
1930 case TCPCHK_SEND_BINARY:
1931 if (istlen(send->data) >= b_size(&check->bo)) {
1932 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1933 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1934 tcpcheck_get_step_id(check, rule));
1935 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1936 ret = TCPCHK_EVAL_STOP;
1937 goto out;
1938 }
1939 b_putist(&check->bo, send->data);
1940 break;
1941 case TCPCHK_SEND_STRING_LF:
1942 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1943 if (!b_data(&check->bo))
1944 goto out;
1945 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001946 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001947 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001948
Christopher Faulet61cc8522020-04-20 14:54:42 +02001949 tmp = alloc_trash_chunk();
1950 if (!tmp)
1951 goto error_lf;
1952 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1953 if (!b_data(tmp))
1954 goto out;
1955 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001956 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001957 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001958 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001959 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001960 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001961 case TCPCHK_SEND_HTTP: {
1962 struct htx_sl *sl;
1963 struct ist meth, uri, vsn, clen, body;
1964 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001965
Christopher Faulet61cc8522020-04-20 14:54:42 +02001966 tmp = alloc_trash_chunk();
1967 if (!tmp)
1968 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001969
Christopher Faulet61cc8522020-04-20 14:54:42 +02001970 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1971 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1972 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001973 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1974 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1975 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1976 }
1977 else
1978 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001979 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001980
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001981 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1982 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001983 slflags |= HTX_SL_F_VER_11;
1984 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1985 if (!isttest(send->http.body))
1986 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001987
Christopher Faulet61cc8522020-04-20 14:54:42 +02001988 htx = htx_from_buf(&check->bo);
1989 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1990 if (!sl)
1991 goto error_htx;
1992 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001993 if (!http_update_host(htx, sl, uri))
1994 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001995
Christopher Faulet61cc8522020-04-20 14:54:42 +02001996 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1997 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001998 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001999
Christopher Faulet61cc8522020-04-20 14:54:42 +02002000 list_for_each_entry(hdr, &send->http.hdrs, list) {
2001 chunk_reset(tmp);
2002 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2003 if (!b_data(tmp))
2004 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002005 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2006 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002007 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002008 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2009 if (!http_update_authority(htx, sl, hdr_value))
2010 goto error_htx;
2011 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002012 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002013
Christopher Faulet61cc8522020-04-20 14:54:42 +02002014 }
2015 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2016 chunk_reset(tmp);
2017 httpchk_build_status_header(check->server, tmp);
2018 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2019 goto error_htx;
2020 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002021
2022
2023 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2024 chunk_reset(tmp);
2025 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2026 body = ist2(b_orig(tmp), b_data(tmp));
2027 }
2028 else
2029 body = send->http.body;
2030 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2031
2032 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2033 !htx_add_header(htx, ist("Content-length"), clen))
2034 goto error_htx;
2035
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002036
Christopher Faulet61cc8522020-04-20 14:54:42 +02002037 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002038 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002039 !htx_add_endof(htx, HTX_BLK_EOM))
2040 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002041
Christopher Faulet61cc8522020-04-20 14:54:42 +02002042 htx_to_buf(htx, &check->bo);
2043 break;
2044 }
2045 case TCPCHK_SEND_UNDEF:
2046 /* Should never happen. */
2047 ret = TCPCHK_EVAL_STOP;
2048 goto out;
2049 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002050
Christopher Faulet6d471212020-04-22 11:09:25 +02002051
2052 if (conn->mux->snd_buf(cs, &check->bo,
2053 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002054 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002055 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002056 goto out;
2057 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002058 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002059 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002060 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2061 ret = TCPCHK_EVAL_WAIT;
2062 goto out;
2063 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002064
Christopher Faulet61cc8522020-04-20 14:54:42 +02002065 out:
2066 free_trash_chunk(tmp);
2067 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002068
Christopher Faulet61cc8522020-04-20 14:54:42 +02002069 error_htx:
2070 if (htx) {
2071 htx_reset(htx);
2072 htx_to_buf(htx, &check->bo);
2073 }
2074 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2075 tcpcheck_get_step_id(check, rule));
2076 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2077 ret = TCPCHK_EVAL_STOP;
2078 goto out;
2079
2080 error_lf:
2081 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2082 tcpcheck_get_step_id(check, rule));
2083 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2084 ret = TCPCHK_EVAL_STOP;
2085 goto out;
2086
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002087}
2088
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002089/* Try to receive data before evaluating a tcp-check expect rule. Returns
2090 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002091 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2092 * TCPCHK_EVAL_STOP if an error occurred.
2093 */
2094static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002095{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002096 struct conn_stream *cs = check->cs;
2097 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002098 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002099 size_t max, read, cur_read = 0;
2100 int is_empty;
2101 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002102
Christopher Faulet61cc8522020-04-20 14:54:42 +02002103 if (check->wait_list.events & SUB_RETRY_RECV)
2104 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002105
Christopher Faulet61cc8522020-04-20 14:54:42 +02002106 if (cs->flags & CS_FL_EOS)
2107 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002108
Christopher Faulet61cc8522020-04-20 14:54:42 +02002109 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002110
Christopher Faulet61cc8522020-04-20 14:54:42 +02002111 /* prepare to detect if the mux needs more room */
2112 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002113
Christopher Faulet61cc8522020-04-20 14:54:42 +02002114 while ((cs->flags & CS_FL_RCV_MORE) ||
2115 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2116 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2117 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2118 cur_read += read;
2119 if (!read ||
2120 (cs->flags & CS_FL_WANT_ROOM) ||
2121 (--read_poll <= 0) ||
2122 (read < max && read >= global.tune.recv_enough))
2123 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002124 }
2125
Christopher Faulet61cc8522020-04-20 14:54:42 +02002126 end_recv:
2127 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2128 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2129 /* Report network errors only if we got no other data. Otherwise
2130 * we'll let the upper layers decide whether the response is OK
2131 * or not. It is very common that an RST sent by the server is
2132 * reported as an error just after the last data chunk.
2133 */
2134 goto stop;
2135 }
2136 if (!cur_read) {
2137 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2138 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2139 goto wait_more_data;
2140 }
2141 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002142 int status;
2143
Christopher Faulet61cc8522020-04-20 14:54:42 +02002144 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2145 tcpcheck_get_step_id(check, rule));
2146 if (rule->comment)
2147 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002148
2149 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2150 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002151 goto stop;
2152 }
2153 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002154
2155 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002156 return ret;
2157
Christopher Faulet61cc8522020-04-20 14:54:42 +02002158 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002159 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002160 goto out;
2161
2162 wait_more_data:
2163 ret = TCPCHK_EVAL_WAIT;
2164 goto out;
2165}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002166
Christopher Faulet61cc8522020-04-20 14:54:42 +02002167/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2168 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2169 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2170 * error occurred.
2171 */
2172static 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 +02002173{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002174 struct htx *htx = htxbuf(&check->bi);
2175 struct htx_sl *sl;
2176 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002177 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002178 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002179 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002180 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002181 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002182 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002183
Christopher Faulet61cc8522020-04-20 14:54:42 +02002184 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002185
Christopher Faulet61cc8522020-04-20 14:54:42 +02002186 if (htx->flags & HTX_FL_PARSING_ERROR) {
2187 status = HCHK_STATUS_L7RSP;
2188 goto error;
2189 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002190
Christopher Faulet61cc8522020-04-20 14:54:42 +02002191 if (htx_is_empty(htx)) {
2192 if (last_read) {
2193 status = HCHK_STATUS_L7RSP;
2194 goto error;
2195 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002196 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002197 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002198
Christopher Faulet61cc8522020-04-20 14:54:42 +02002199 sl = http_get_stline(htx);
2200 check->code = sl->info.res.status;
2201
2202 if (check->server &&
2203 (check->server->proxy->options & PR_O_DISABLE404) &&
2204 (check->server->next_state != SRV_ST_STOPPED) &&
2205 (check->code == 404)) {
2206 /* 404 may be accepted as "stopping" only if the server was up */
2207 goto out;
2208 }
2209
2210 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2211 /* Make GCC happy ; initialize match to a failure state. */
2212 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002213 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002214
2215 switch (expect->type) {
2216 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002217 match = 0;
2218 for (i = 0; i < expect->codes.num; i++) {
2219 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2220 sl->info.res.status <= expect->codes.codes[i][1]) {
2221 match = 1;
2222 break;
2223 }
2224 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002225
2226 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002227 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002228 if (LIST_ISEMPTY(&expect->onerror_fmt))
2229 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002230 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002231 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002232 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2233
2234 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002235 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002236 if (LIST_ISEMPTY(&expect->onerror_fmt))
2237 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002238 break;
2239
Christopher Faulet39708192020-05-05 10:47:36 +02002240 case TCPCHK_EXPECT_HTTP_HEADER: {
2241 struct http_hdr_ctx ctx;
2242 struct ist npat, vpat, value;
2243 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2244
2245 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2246 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002247 if (!nbuf) {
2248 status = HCHK_STATUS_L7RSP;
2249 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002250 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002251 }
Christopher Faulet39708192020-05-05 10:47:36 +02002252 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 +02002253 if (!b_data(nbuf)) {
2254 status = HCHK_STATUS_L7RSP;
2255 desc = ist("log-format string evaluated to an empty string");
2256 goto error;
2257 }
Christopher Faulet39708192020-05-05 10:47:36 +02002258 npat = ist2(b_orig(nbuf), b_data(nbuf));
2259 }
2260 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2261 npat = expect->hdr.name;
2262
2263 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2264 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002265 if (!vbuf) {
2266 status = HCHK_STATUS_L7RSP;
2267 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002268 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002269 }
Christopher Faulet39708192020-05-05 10:47:36 +02002270 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 +02002271 if (!b_data(vbuf)) {
2272 status = HCHK_STATUS_L7RSP;
2273 desc = ist("log-format string evaluated to an empty string");
2274 goto error;
2275 }
Christopher Faulet39708192020-05-05 10:47:36 +02002276 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2277 }
2278 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2279 vpat = expect->hdr.value;
2280
2281 match = 0;
2282 ctx.blk = NULL;
2283 while (1) {
2284 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2285 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2286 if (!http_find_str_header(htx, npat, &ctx, full))
2287 goto end_of_match;
2288 break;
2289 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2290 if (!http_find_pfx_header(htx, npat, &ctx, full))
2291 goto end_of_match;
2292 break;
2293 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2294 if (!http_find_sfx_header(htx, npat, &ctx, full))
2295 goto end_of_match;
2296 break;
2297 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2298 if (!http_find_sub_header(htx, npat, &ctx, full))
2299 goto end_of_match;
2300 break;
2301 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2302 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2303 goto end_of_match;
2304 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002305 default:
2306 /* should never happen */
2307 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002308 }
2309
Christopher Faulet083eff32020-05-07 15:41:39 +02002310 /* A header has matched the name pattern, let's test its
2311 * value now (always defined from there). If there is no
2312 * value pattern, it is a good match.
2313 */
2314
Christopher Faulet39708192020-05-05 10:47:36 +02002315 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2316 match = 1;
2317 goto end_of_match;
2318 }
2319
2320 value = ctx.value;
2321 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2322 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2323 if (isteq(value, vpat)) {
2324 match = 1;
2325 goto end_of_match;
2326 }
2327 break;
2328 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2329 if (istlen(value) < istlen(vpat))
2330 break;
2331 value = ist2(istptr(value), istlen(vpat));
2332 if (isteq(value, vpat)) {
2333 match = 1;
2334 goto end_of_match;
2335 }
2336 break;
2337 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2338 if (istlen(value) < istlen(vpat))
2339 break;
2340 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2341 if (isteq(value, vpat)) {
2342 match = 1;
2343 goto end_of_match;
2344 }
2345 break;
2346 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2347 if (isttest(istist(value, vpat))) {
2348 match = 1;
2349 goto end_of_match;
2350 }
2351 break;
2352 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2353 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2354 match = 1;
2355 goto end_of_match;
2356 }
2357 break;
2358 }
2359 }
2360
2361 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002362 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002363 if (LIST_ISEMPTY(&expect->onerror_fmt))
2364 desc = htx_sl_res_reason(sl);
2365 break;
2366 }
2367
Christopher Faulet61cc8522020-04-20 14:54:42 +02002368 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002369 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002370 case TCPCHK_EXPECT_HTTP_BODY_LF:
2371 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002372 chunk_reset(&trash);
2373 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2374 enum htx_blk_type type = htx_get_blk_type(blk);
2375
2376 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2377 break;
2378 if (type == HTX_BLK_DATA) {
2379 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2380 break;
2381 }
2382 }
2383
2384 if (!b_data(&trash)) {
2385 if (!last_read)
2386 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002387 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002388 if (LIST_ISEMPTY(&expect->onerror_fmt))
2389 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002390 goto error;
2391 }
2392
Christopher Fauletaaab0832020-05-05 15:54:22 +02002393 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2394 tmp = alloc_trash_chunk();
2395 if (!tmp) {
2396 status = HCHK_STATUS_L7RSP;
2397 desc = ist("Failed to allocate buffer to eval log-format string");
2398 goto error;
2399 }
2400 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2401 if (!b_data(tmp)) {
2402 status = HCHK_STATUS_L7RSP;
2403 desc = ist("log-format string evaluated to an empty string");
2404 goto error;
2405 }
2406 }
2407
Christopher Faulet61cc8522020-04-20 14:54:42 +02002408 if (!last_read &&
2409 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002410 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002411 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2412 ret = TCPCHK_EVAL_WAIT;
2413 goto out;
2414 }
2415
2416 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002417 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002418 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2419 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002420 else
2421 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2422
2423 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002424 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002425 if (LIST_ISEMPTY(&expect->onerror_fmt))
2426 desc = (inverse
2427 ? ist("HTTP check matched unwanted content")
2428 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002429 break;
2430
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002431
Christopher Faulet61cc8522020-04-20 14:54:42 +02002432 default:
2433 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002434 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002435 goto error;
2436 }
2437
Christopher Faulet61cc8522020-04-20 14:54:42 +02002438 /* Wait for more data on mismatch only if no minimum is defined (-1),
2439 * otherwise the absence of match is already conclusive.
2440 */
2441 if (!match && !last_read && (expect->min_recv == -1)) {
2442 ret = TCPCHK_EVAL_WAIT;
2443 goto out;
2444 }
2445
2446 if (!(match ^ inverse))
2447 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002448
2449 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002450 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002451 free_trash_chunk(nbuf);
2452 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002453 free_trash_chunk(msg);
2454 return ret;
2455
2456 error:
2457 ret = TCPCHK_EVAL_STOP;
2458 msg = alloc_trash_chunk();
2459 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002460 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002461 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2462 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002463
2464 wait_more_data:
2465 ret = TCPCHK_EVAL_WAIT;
2466 goto out;
2467}
2468
Christopher Faulet61cc8522020-04-20 14:54:42 +02002469/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2470 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2471 * if an error occurred.
2472 */
2473static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2474{
2475 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2476 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002477 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002478 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002479 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002480 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002481
Christopher Faulet61cc8522020-04-20 14:54:42 +02002482 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002483
Christopher Faulet61cc8522020-04-20 14:54:42 +02002484 /* The current expect might need more data than the previous one, check again
2485 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002486 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002487 if (!last_read) {
2488 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2489 (b_data(&check->bi) < istlen(expect->data))) {
2490 ret = TCPCHK_EVAL_WAIT;
2491 goto out;
2492 }
2493 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2494 ret = TCPCHK_EVAL_WAIT;
2495 goto out;
2496 }
2497 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002498
Christopher Faulet61cc8522020-04-20 14:54:42 +02002499 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2500 /* Make GCC happy ; initialize match to a failure state. */
2501 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002502 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002503
Christopher Faulet61cc8522020-04-20 14:54:42 +02002504 switch (expect->type) {
2505 case TCPCHK_EXPECT_STRING:
2506 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002507 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 +02002508 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002509 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002510 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 +02002511 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002512
Christopher Faulet67a23452020-05-05 18:10:01 +02002513 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002514 chunk_reset(&trash);
2515 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002516 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002517 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002518
2519 case TCPCHK_EXPECT_STRING_LF:
2520 case TCPCHK_EXPECT_BINARY_LF:
2521 match = 0;
2522 tmp = alloc_trash_chunk();
2523 if (!tmp) {
2524 status = HCHK_STATUS_L7RSP;
2525 desc = ist("Failed to allocate buffer to eval format string");
2526 goto error;
2527 }
2528 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2529 if (!b_data(tmp)) {
2530 status = HCHK_STATUS_L7RSP;
2531 desc = ist("log-format string evaluated to an empty string");
2532 goto error;
2533 }
2534 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2535 int len = tmp->data;
2536 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2537 status = HCHK_STATUS_L7RSP;
2538 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2539 goto error;
2540 }
2541 tmp->data = len;
2542 }
2543 if (b_data(&check->bi) < tmp->data) {
2544 if (!last_read) {
2545 ret = TCPCHK_EVAL_WAIT;
2546 goto out;
2547 }
2548 break;
2549 }
2550 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2551 break;
2552
Christopher Faulet61cc8522020-04-20 14:54:42 +02002553 case TCPCHK_EXPECT_CUSTOM:
2554 if (expect->custom)
2555 ret = expect->custom(check, rule, last_read);
2556 goto out;
2557 default:
2558 /* Should never happen. */
2559 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002560 goto out;
2561 }
2562
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002563
Christopher Faulet61cc8522020-04-20 14:54:42 +02002564 /* Wait for more data on mismatch only if no minimum is defined (-1),
2565 * otherwise the absence of match is already conclusive.
2566 */
2567 if (!match && !last_read && (expect->min_recv == -1)) {
2568 ret = TCPCHK_EVAL_WAIT;
2569 goto out;
2570 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002571
Christopher Faulet61cc8522020-04-20 14:54:42 +02002572 /* Result as expected, next rule. */
2573 if (match ^ inverse)
2574 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002575
Christopher Fauletaaab0832020-05-05 15:54:22 +02002576 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002577 /* From this point on, we matched something we did not want, this is an error state. */
2578 ret = TCPCHK_EVAL_STOP;
2579 msg = alloc_trash_chunk();
2580 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002581 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002582 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002583 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002584
Christopher Faulet61cc8522020-04-20 14:54:42 +02002585 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002586 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002587 return ret;
2588}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002589
Christopher Faulet61cc8522020-04-20 14:54:42 +02002590/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002591 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002592 * waits.
2593 */
2594static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2595{
2596 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2597 struct act_rule *act_rule;
2598 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002599
Christopher Faulet61cc8522020-04-20 14:54:42 +02002600 act_rule =rule->action_kw.rule;
2601 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2602 if (act_ret != ACT_RET_CONT) {
2603 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2604 tcpcheck_get_step_id(check, rule));
2605 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2606 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002607 }
2608
Christopher Faulet61cc8522020-04-20 14:54:42 +02002609 return ret;
2610}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002611
Christopher Faulet61cc8522020-04-20 14:54:42 +02002612/* Executes a tcp-check ruleset. Note that this is called both from the
2613 * connection's wake() callback and from the check scheduling task. It returns
2614 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2615 * presenting the risk of an fd replacement.
2616 *
2617 * Please do NOT place any return statement in this function and only leave
2618 * via the out_end_tcpcheck label after setting retcode.
2619 */
2620static int tcpcheck_main(struct check *check)
2621{
2622 struct tcpcheck_rule *rule;
2623 struct conn_stream *cs = check->cs;
2624 struct connection *conn = cs_conn(cs);
2625 int must_read = 1, last_read = 0;
2626 int ret, retcode = 0;
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002627 enum tcpcheck_eval_ret eval_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002628
Christopher Faulet61cc8522020-04-20 14:54:42 +02002629 /* here, we know that the check is complete or that it failed */
2630 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002631 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002632
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002633 /* Note: the conn-stream and the connection may only be undefined before
2634 * the first rule evaluation (it is always a connect rule) or when the
2635 * conn-stream allocation failed on the first connect.
2636 */
2637
Christopher Faulet61cc8522020-04-20 14:54:42 +02002638 /* 1- check for connection error, if any */
2639 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2640 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002641
Christopher Faulet61cc8522020-04-20 14:54:42 +02002642 /* 2- check if we are waiting for the connection establishment. It only
2643 * happens during TCPCHK_ACT_CONNECT. */
2644 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002645 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002646 struct tcpcheck_rule *next;
2647
2648 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2649 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002650 if (!(check->wait_list.events & SUB_RETRY_SEND))
2651 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002652 goto out;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002653 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002654 else {
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002655 eval_ret = tcpcheck_eval_recv(check, check->current_step);
2656 if (eval_ret == TCPCHK_EVAL_STOP)
2657 goto out_end_tcpcheck;
2658 else if (eval_ret == TCPCHK_EVAL_WAIT)
2659 goto out;
2660 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2661 must_read = 0;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002662 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002663 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002664 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002665 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002666
2667 /* 3- check for pending outgoing data. It only happens during
2668 * TCPCHK_ACT_SEND. */
2669 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002670 if (b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002671 /* We're already waiting to be able to send, give up */
2672 if (check->wait_list.events & SUB_RETRY_SEND)
2673 goto out;
2674
Christopher Faulet6d471212020-04-22 11:09:25 +02002675 ret = conn->mux->snd_buf(cs, &check->bo,
2676 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002677 if (ret <= 0) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002678 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002679 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002680 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002681 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002682 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002683 goto out;
2684 }
2685 }
2686 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002687 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002688
Christopher Faulet61cc8522020-04-20 14:54:42 +02002689 /* 4- check if a rule must be resume. It happens if check->current_step
2690 * is defined. */
2691 else if (check->current_step)
2692 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002693
Christopher Faulet61cc8522020-04-20 14:54:42 +02002694 /* 5- It is the first evaluation. We must create a session and preset
2695 * tcp-check variables */
2696 else {
2697 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002698
Christopher Faulet61cc8522020-04-20 14:54:42 +02002699 /* First evaluation, create a session */
2700 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2701 if (!check->sess) {
2702 chunk_printf(&trash, "TCPCHK error allocating check session");
2703 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2704 goto out_end_tcpcheck;
2705 }
2706 vars_init(&check->vars, SCOPE_CHECK);
2707 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002708
Christopher Faulet61cc8522020-04-20 14:54:42 +02002709 /* Preset tcp-check variables */
2710 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2711 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002712
Christopher Faulet61cc8522020-04-20 14:54:42 +02002713 memset(&smp, 0, sizeof(smp));
2714 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2715 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002716 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002717 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002718 }
2719
Christopher Faulet61cc8522020-04-20 14:54:42 +02002720 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002721
Christopher Faulet61cc8522020-04-20 14:54:42 +02002722 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002723 check->code = 0;
2724 switch (rule->action) {
2725 case TCPCHK_ACT_CONNECT:
2726 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002727
Christopher Faulet61cc8522020-04-20 14:54:42 +02002728 /* close but not release yet previous connection */
2729 if (check->cs) {
2730 cs_close(check->cs);
2731 retcode = -1; /* do not reuse the fd in the caller! */
2732 }
2733 eval_ret = tcpcheck_eval_connect(check, rule);
Christopher Faulet3cbdd222020-05-26 11:14:50 +02002734
2735 /* Refresh conn-stream and connection */
2736 cs = check->cs;
2737 conn = cs_conn(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002738 must_read = 1; last_read = 0;
2739 break;
2740 case TCPCHK_ACT_SEND:
2741 check->current_step = rule;
2742 eval_ret = tcpcheck_eval_send(check, rule);
2743 must_read = 1;
2744 break;
2745 case TCPCHK_ACT_EXPECT:
2746 check->current_step = rule;
2747 if (must_read) {
2748 if (check->proxy->timeout.check)
2749 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002750
Christopher Faulet61cc8522020-04-20 14:54:42 +02002751 eval_ret = tcpcheck_eval_recv(check, rule);
2752 if (eval_ret == TCPCHK_EVAL_STOP)
2753 goto out_end_tcpcheck;
2754 else if (eval_ret == TCPCHK_EVAL_WAIT)
2755 goto out;
2756 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2757 must_read = 0;
2758 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002759
Christopher Faulet61cc8522020-04-20 14:54:42 +02002760 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2761 ? tcpcheck_eval_expect_http(check, rule, last_read)
2762 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002763
Christopher Faulet61cc8522020-04-20 14:54:42 +02002764 if (eval_ret == TCPCHK_EVAL_WAIT) {
2765 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002766 if (!(check->wait_list.events & SUB_RETRY_RECV))
2767 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002768 }
2769 break;
2770 case TCPCHK_ACT_ACTION_KW:
2771 /* Don't update the current step */
2772 eval_ret = tcpcheck_eval_action_kw(check, rule);
2773 break;
2774 default:
2775 /* Otherwise, just go to the next one and don't update
2776 * the current step
2777 */
2778 eval_ret = TCPCHK_EVAL_CONTINUE;
2779 break;
2780 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002781
Christopher Faulet61cc8522020-04-20 14:54:42 +02002782 switch (eval_ret) {
2783 case TCPCHK_EVAL_CONTINUE:
2784 break;
2785 case TCPCHK_EVAL_WAIT:
2786 goto out;
2787 case TCPCHK_EVAL_STOP:
2788 goto out_end_tcpcheck;
2789 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002790 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002791
Christopher Faulet61cc8522020-04-20 14:54:42 +02002792 /* All rules was evaluated */
2793 if (check->current_step) {
2794 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002795
Christopher Faulet61cc8522020-04-20 14:54:42 +02002796 if (rule->action == TCPCHK_ACT_EXPECT) {
2797 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002798 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002799
Christopher Faulet61cc8522020-04-20 14:54:42 +02002800 if (check->server &&
2801 (check->server->proxy->options & PR_O_DISABLE404) &&
2802 (check->server->next_state != SRV_ST_STOPPED) &&
2803 (check->code == 404)) {
2804 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2805 goto out_end_tcpcheck;
2806 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002807
Christopher Faulet61cc8522020-04-20 14:54:42 +02002808 msg = alloc_trash_chunk();
2809 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002810 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002811 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2812 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002813 free_trash_chunk(msg);
2814 }
2815 else if (rule->action == TCPCHK_ACT_CONNECT) {
2816 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002817 enum healthcheck_status status = HCHK_STATUS_L4OK;
2818#ifdef USE_OPENSSL
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002819 if (ssl_sock_is_ssl(conn))
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002820 status = HCHK_STATUS_L6OK;
2821#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002822 set_server_check_status(check, status, msg);
2823 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002824 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002825 else
2826 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002827
Christopher Faulet61cc8522020-04-20 14:54:42 +02002828 out_end_tcpcheck:
2829 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2830 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002831
Christopher Faulet61cc8522020-04-20 14:54:42 +02002832 out:
2833 return retcode;
2834}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002835
Christopher Faulet14cd3162020-04-16 14:50:06 +02002836
Christopher Faulet61cc8522020-04-20 14:54:42 +02002837/**************************************************************************/
2838/************** Health-checks based on an external process ****************/
2839/**************************************************************************/
2840static struct list pid_list = LIST_HEAD_INIT(pid_list);
2841static struct pool_head *pool_head_pid_list;
2842__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002843
Christopher Faulet61cc8522020-04-20 14:54:42 +02002844struct extcheck_env {
2845 char *name; /* environment variable name */
2846 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2847};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002848
Christopher Faulet61cc8522020-04-20 14:54:42 +02002849/* environment variables memory requirement for different types of data */
2850#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2851 * such environment variables are not updatable. */
2852#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2853#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2854#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002855
Christopher Faulet61cc8522020-04-20 14:54:42 +02002856/* external checks environment variables */
2857enum {
2858 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002859
Christopher Faulet61cc8522020-04-20 14:54:42 +02002860 /* Proxy specific environment variables */
2861 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2862 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2863 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2864 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002865
Christopher Faulet61cc8522020-04-20 14:54:42 +02002866 /* Server specific environment variables */
2867 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2868 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2869 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2870 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2871 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2872 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002873
Christopher Faulet61cc8522020-04-20 14:54:42 +02002874 EXTCHK_SIZE
2875};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002876
Christopher Faulet61cc8522020-04-20 14:54:42 +02002877const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2878 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2879 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2880 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2881 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2882 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2883 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2884 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2885 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2886 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2887 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2888 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2889};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002890
Christopher Faulet61cc8522020-04-20 14:54:42 +02002891void block_sigchld(void)
2892{
2893 sigset_t set;
2894 sigemptyset(&set);
2895 sigaddset(&set, SIGCHLD);
2896 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2897}
Willy Tarreaube373152018-09-06 11:45:30 +02002898
Christopher Faulet61cc8522020-04-20 14:54:42 +02002899void unblock_sigchld(void)
2900{
2901 sigset_t set;
2902 sigemptyset(&set);
2903 sigaddset(&set, SIGCHLD);
2904 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002905}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002906
Christopher Faulet61cc8522020-04-20 14:54:42 +02002907static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002908{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002909 struct pid_list *elem;
2910 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002911
Christopher Faulet61cc8522020-04-20 14:54:42 +02002912 elem = pool_alloc(pool_head_pid_list);
2913 if (!elem)
2914 return NULL;
2915 elem->pid = pid;
2916 elem->t = t;
2917 elem->exited = 0;
2918 check->curpid = elem;
2919 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002920
Christopher Faulet61cc8522020-04-20 14:54:42 +02002921 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2922 LIST_ADD(&pid_list, &elem->list);
2923 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002924
Christopher Faulet61cc8522020-04-20 14:54:42 +02002925 return elem;
2926}
Christopher Faulete5870d82020-04-15 11:32:03 +02002927
Christopher Faulet61cc8522020-04-20 14:54:42 +02002928static void pid_list_del(struct pid_list *elem)
2929{
2930 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002931
Christopher Faulet61cc8522020-04-20 14:54:42 +02002932 if (!elem)
2933 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002934
Christopher Faulet61cc8522020-04-20 14:54:42 +02002935 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2936 LIST_DEL(&elem->list);
2937 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002938
Christopher Faulet61cc8522020-04-20 14:54:42 +02002939 if (!elem->exited)
2940 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002941
Christopher Faulet61cc8522020-04-20 14:54:42 +02002942 check = elem->t->context;
2943 check->curpid = NULL;
2944 pool_free(pool_head_pid_list, elem);
2945}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002946
Christopher Faulet61cc8522020-04-20 14:54:42 +02002947/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2948static void pid_list_expire(pid_t pid, int status)
2949{
2950 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002951
Christopher Faulet61cc8522020-04-20 14:54:42 +02002952 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2953 list_for_each_entry(elem, &pid_list, list) {
2954 if (elem->pid == pid) {
2955 elem->t->expire = now_ms;
2956 elem->status = status;
2957 elem->exited = 1;
2958 task_wakeup(elem->t, TASK_WOKEN_IO);
2959 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002960 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002961 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002962 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2963}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002964
Christopher Faulet61cc8522020-04-20 14:54:42 +02002965static void sigchld_handler(struct sig_handler *sh)
2966{
2967 pid_t pid;
2968 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002969
Christopher Faulet61cc8522020-04-20 14:54:42 +02002970 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2971 pid_list_expire(pid, status);
2972}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002973
Christopher Faulet61cc8522020-04-20 14:54:42 +02002974static int init_pid_list(void)
2975{
2976 if (pool_head_pid_list != NULL)
2977 /* Nothing to do */
2978 return 0;
2979
2980 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2981 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2982 strerror(errno));
2983 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002984 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002985
Christopher Faulet61cc8522020-04-20 14:54:42 +02002986 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2987 if (pool_head_pid_list == NULL) {
2988 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2989 strerror(errno));
2990 return 1;
2991 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002992
Christopher Faulet61cc8522020-04-20 14:54:42 +02002993 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002994}
2995
Christopher Faulet61cc8522020-04-20 14:54:42 +02002996/* helper macro to set an environment variable and jump to a specific label on failure. */
2997#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002998
Christopher Faulet61cc8522020-04-20 14:54:42 +02002999/*
3000 * helper function to allocate enough memory to store an environment variable.
3001 * It will also check that the environment variable is updatable, and silently
3002 * fail if not.
3003 */
3004static int extchk_setenv(struct check *check, int idx, const char *value)
3005{
3006 int len, ret;
3007 char *envname;
3008 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003009
Christopher Faulet61cc8522020-04-20 14:54:42 +02003010 if (idx < 0 || idx >= EXTCHK_SIZE) {
3011 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
3012 return 1;
3013 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003014
Christopher Faulet61cc8522020-04-20 14:54:42 +02003015 envname = extcheck_envs[idx].name;
3016 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003017
Christopher Faulet61cc8522020-04-20 14:54:42 +02003018 /* Check if the environment variable is already set, and silently reject
3019 * the update if this one is not updatable. */
3020 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3021 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003022
Christopher Faulet61cc8522020-04-20 14:54:42 +02003023 /* Instead of sending NOT_USED, sending an empty value is preferable */
3024 if (strcmp(value, "NOT_USED") == 0) {
3025 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003026 }
3027
Christopher Faulet61cc8522020-04-20 14:54:42 +02003028 len = strlen(envname) + 1;
3029 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3030 len += strlen(value);
3031 else
3032 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003033
Christopher Faulet61cc8522020-04-20 14:54:42 +02003034 if (!check->envp[idx])
3035 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003036
Christopher Faulet61cc8522020-04-20 14:54:42 +02003037 if (!check->envp[idx]) {
3038 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3039 return 1;
3040 }
3041 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3042 if (ret < 0) {
3043 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3044 return 1;
3045 }
3046 else if (ret > len) {
3047 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3048 return 1;
3049 }
3050 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003051}
3052
Christopher Faulet61cc8522020-04-20 14:54:42 +02003053static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003054{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003055 struct server *s = check->server;
3056 struct proxy *px = s->proxy;
3057 struct listener *listener = NULL, *l;
3058 int i;
3059 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3060 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003061
Christopher Faulet61cc8522020-04-20 14:54:42 +02003062 list_for_each_entry(l, &px->conf.listeners, by_fe)
3063 /* Use the first INET, INET6 or UNIX listener */
3064 if (l->addr.ss_family == AF_INET ||
3065 l->addr.ss_family == AF_INET6 ||
3066 l->addr.ss_family == AF_UNIX) {
3067 listener = l;
3068 break;
3069 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003070
Christopher Faulet61cc8522020-04-20 14:54:42 +02003071 check->curpid = NULL;
3072 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3073 if (!check->envp) {
3074 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3075 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003076 }
3077
Christopher Faulet61cc8522020-04-20 14:54:42 +02003078 check->argv = calloc(6, sizeof(char *));
3079 if (!check->argv) {
3080 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3081 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003082 }
3083
Christopher Faulet61cc8522020-04-20 14:54:42 +02003084 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003085
Christopher Faulet61cc8522020-04-20 14:54:42 +02003086 if (!listener) {
3087 check->argv[1] = strdup("NOT_USED");
3088 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003089 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003090 else if (listener->addr.ss_family == AF_INET ||
3091 listener->addr.ss_family == AF_INET6) {
3092 addr_to_str(&listener->addr, buf, sizeof(buf));
3093 check->argv[1] = strdup(buf);
3094 port_to_str(&listener->addr, buf, sizeof(buf));
3095 check->argv[2] = strdup(buf);
3096 }
3097 else if (listener->addr.ss_family == AF_UNIX) {
3098 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003099
Christopher Faulet61cc8522020-04-20 14:54:42 +02003100 un = (struct sockaddr_un *)&listener->addr;
3101 check->argv[1] = strdup(un->sun_path);
3102 check->argv[2] = strdup("NOT_USED");
3103 }
3104 else {
3105 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3106 goto err;
3107 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003108
Christopher Faulet61cc8522020-04-20 14:54:42 +02003109 if (!check->argv[1] || !check->argv[2]) {
3110 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3111 goto err;
3112 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003113
Christopher Faulet61cc8522020-04-20 14:54:42 +02003114 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3115 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3116 if (!check->argv[3] || !check->argv[4]) {
3117 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3118 goto err;
3119 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003120
Christopher Faulet61cc8522020-04-20 14:54:42 +02003121 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3122 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3123 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003124
Christopher Faulet61cc8522020-04-20 14:54:42 +02003125 for (i = 0; i < 5; i++) {
3126 if (!check->argv[i]) {
3127 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3128 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003129 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003130 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003131
Christopher Faulet61cc8522020-04-20 14:54:42 +02003132 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3133 /* Add proxy environment variables */
3134 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3135 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3136 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3137 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3138 /* Add server environment variables */
3139 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3140 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3141 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3142 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3143 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3144 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003145
Christopher Faulet61cc8522020-04-20 14:54:42 +02003146 /* Ensure that we don't leave any hole in check->envp */
3147 for (i = 0; i < EXTCHK_SIZE; i++)
3148 if (!check->envp[i])
3149 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003150
Christopher Faulet61cc8522020-04-20 14:54:42 +02003151 return 1;
3152err:
3153 if (check->envp) {
3154 for (i = 0; i < EXTCHK_SIZE; i++)
3155 free(check->envp[i]);
3156 free(check->envp);
3157 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003158 }
3159
Christopher Faulet61cc8522020-04-20 14:54:42 +02003160 if (check->argv) {
3161 for (i = 1; i < 5; i++)
3162 free(check->argv[i]);
3163 free(check->argv);
3164 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003165 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003166 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003167}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003168
Christopher Faulet61cc8522020-04-20 14:54:42 +02003169/*
3170 * establish a server health-check that makes use of a process.
3171 *
3172 * It can return one of :
3173 * - SF_ERR_NONE if everything's OK
3174 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3175 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3176 *
3177 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003178 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003179static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003180{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003181 char buf[256];
3182 struct check *check = t->context;
3183 struct server *s = check->server;
3184 struct proxy *px = s->proxy;
3185 int status;
3186 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003187
Christopher Faulet61cc8522020-04-20 14:54:42 +02003188 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003189
Christopher Faulet61cc8522020-04-20 14:54:42 +02003190 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003191
Christopher Faulet61cc8522020-04-20 14:54:42 +02003192 pid = fork();
3193 if (pid < 0) {
3194 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3195 (global.tune.options & GTUNE_INSECURE_FORK) ?
3196 "" : " (likely caused by missing 'insecure-fork-wanted')",
3197 strerror(errno));
3198 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003199 goto out;
3200 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003201 if (pid == 0) {
3202 /* Child */
3203 extern char **environ;
3204 struct rlimit limit;
3205 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003206
Christopher Faulet61cc8522020-04-20 14:54:42 +02003207 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3208 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003209
Christopher Faulet61cc8522020-04-20 14:54:42 +02003210 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003211
Christopher Faulet61cc8522020-04-20 14:54:42 +02003212 /* restore the initial FD limits */
3213 limit.rlim_cur = rlim_fd_cur_at_boot;
3214 limit.rlim_max = rlim_fd_max_at_boot;
3215 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3216 getrlimit(RLIMIT_NOFILE, &limit);
3217 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3218 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3219 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3220 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003221
Christopher Faulet61cc8522020-04-20 14:54:42 +02003222 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003223
Christopher Faulet61cc8522020-04-20 14:54:42 +02003224 /* Update some environment variables and command args: curconn, server addr and server port */
3225 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003226
Christopher Faulet61cc8522020-04-20 14:54:42 +02003227 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3228 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003229
Christopher Faulet61cc8522020-04-20 14:54:42 +02003230 *check->argv[4] = 0;
3231 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3232 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3233 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003234
Christopher Faulet61cc8522020-04-20 14:54:42 +02003235 haproxy_unblock_signals();
3236 execvp(px->check_command, check->argv);
3237 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3238 strerror(errno));
3239 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003240 }
3241
Christopher Faulet61cc8522020-04-20 14:54:42 +02003242 /* Parent */
3243 if (check->result == CHK_RES_UNKNOWN) {
3244 if (pid_list_add(pid, t) != NULL) {
3245 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3246
3247 if (px->timeout.check && px->timeout.connect) {
3248 int t_con = tick_add(now_ms, px->timeout.connect);
3249 t->expire = tick_first(t->expire, t_con);
3250 }
3251 status = SF_ERR_NONE;
3252 goto out;
3253 }
3254 else {
3255 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3256 }
3257 kill(pid, SIGTERM); /* process creation error */
3258 }
3259 else
3260 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3261
3262out:
3263 unblock_sigchld();
3264 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003265}
3266
Christopher Faulet61cc8522020-04-20 14:54:42 +02003267/*
3268 * manages a server health-check that uses an external process. Returns
3269 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003270 *
3271 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003272 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003273 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003274static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003275{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003276 struct check *check = context;
3277 struct server *s = check->server;
3278 int rv;
3279 int ret;
3280 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003281
Christopher Faulet61cc8522020-04-20 14:54:42 +02003282 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3283 if (!(check->state & CHK_ST_INPROGRESS)) {
3284 /* no check currently running */
3285 if (!expired) /* woke up too early */
3286 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003287
Christopher Faulet61cc8522020-04-20 14:54:42 +02003288 /* we don't send any health-checks when the proxy is
3289 * stopped, the server should not be checked or the check
3290 * is disabled.
3291 */
3292 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3293 s->proxy->state == PR_STSTOPPED)
3294 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003295
Christopher Faulet61cc8522020-04-20 14:54:42 +02003296 /* we'll initiate a new check */
3297 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003298
Christopher Faulet61cc8522020-04-20 14:54:42 +02003299 check->state |= CHK_ST_INPROGRESS;
3300
3301 ret = connect_proc_chk(t);
3302 if (ret == SF_ERR_NONE) {
3303 /* the process was forked, we allow up to min(inter,
3304 * timeout.connect) for it to report its status, but
3305 * only when timeout.check is set as it may be to short
3306 * for a full check otherwise.
3307 */
3308 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3309
3310 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3311 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3312 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003313 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003314 task_set_affinity(t, tid_bit);
3315 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003316 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003317
Christopher Faulet61cc8522020-04-20 14:54:42 +02003318 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003319
Christopher Faulet61cc8522020-04-20 14:54:42 +02003320 check->state &= ~CHK_ST_INPROGRESS;
3321 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003322
Christopher Faulet61cc8522020-04-20 14:54:42 +02003323 /* we allow up to min(inter, timeout.connect) for a connection
3324 * to establish but only when timeout.check is set
3325 * as it may be to short for a full check otherwise
3326 */
3327 while (tick_is_expired(t->expire, now_ms)) {
3328 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003329
Christopher Faulet61cc8522020-04-20 14:54:42 +02003330 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3331 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003332
Christopher Faulet61cc8522020-04-20 14:54:42 +02003333 if (s->proxy->timeout.check)
3334 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003335 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003336 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003337 else {
3338 /* there was a test running.
3339 * First, let's check whether there was an uncaught error,
3340 * which can happen on connect timeout or error.
3341 */
3342 if (check->result == CHK_RES_UNKNOWN) {
3343 /* good connection is enough for pure TCP check */
3344 struct pid_list *elem = check->curpid;
3345 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003346
Christopher Faulet61cc8522020-04-20 14:54:42 +02003347 if (elem->exited) {
3348 status = elem->status; /* Save in case the process exits between use below */
3349 if (!WIFEXITED(status))
3350 check->code = -1;
3351 else
3352 check->code = WEXITSTATUS(status);
3353 if (!WIFEXITED(status) || WEXITSTATUS(status))
3354 status = HCHK_STATUS_PROCERR;
3355 else
3356 status = HCHK_STATUS_PROCOK;
3357 } else if (expired) {
3358 status = HCHK_STATUS_PROCTOUT;
3359 ha_warning("kill %d\n", (int)elem->pid);
3360 kill(elem->pid, SIGTERM);
3361 }
3362 set_server_check_status(check, status, NULL);
3363 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003364
Christopher Faulet61cc8522020-04-20 14:54:42 +02003365 if (check->result == CHK_RES_FAILED) {
3366 /* a failure or timeout detected */
3367 check_notify_failure(check);
3368 }
3369 else if (check->result == CHK_RES_CONDPASS) {
3370 /* check is OK but asks for stopping mode */
3371 check_notify_stopping(check);
3372 }
3373 else if (check->result == CHK_RES_PASSED) {
3374 /* a success was detected */
3375 check_notify_success(check);
3376 }
3377 task_set_affinity(t, 1);
3378 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003379
Christopher Faulet61cc8522020-04-20 14:54:42 +02003380 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003381
Christopher Faulet61cc8522020-04-20 14:54:42 +02003382 rv = 0;
3383 if (global.spread_checks > 0) {
3384 rv = srv_getinter(check) * global.spread_checks / 100;
3385 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3386 }
3387 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3388 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003389
Christopher Faulet61cc8522020-04-20 14:54:42 +02003390 reschedule:
3391 while (tick_is_expired(t->expire, now_ms))
3392 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003393
Christopher Faulet61cc8522020-04-20 14:54:42 +02003394 out_unlock:
3395 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3396 return t;
3397}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003398
Baptiste Assmann248f1172018-03-01 21:49:01 +01003399
Christopher Faulet61cc8522020-04-20 14:54:42 +02003400/**************************************************************************/
3401/***************** Health-checks based on connections *********************/
3402/**************************************************************************/
3403/* This function is used only for server health-checks. It handles connection
3404 * status updates including errors. If necessary, it wakes the check task up.
3405 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3406 * connection (eg: reconnect). It relies on tcpcheck_main().
3407 */
3408static int wake_srv_chk(struct conn_stream *cs)
3409{
3410 struct connection *conn = cs->conn;
3411 struct check *check = cs->data;
3412 struct email_alertq *q = container_of(check, typeof(*q), check);
3413 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003414
Christopher Faulet61cc8522020-04-20 14:54:42 +02003415 if (check->server)
3416 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3417 else
3418 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003419
Christopher Faulet61cc8522020-04-20 14:54:42 +02003420 /* we may have to make progress on the TCP checks */
3421 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003422
Christopher Faulet61cc8522020-04-20 14:54:42 +02003423 cs = check->cs;
3424 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003425
Christopher Faulet61cc8522020-04-20 14:54:42 +02003426 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3427 /* We may get error reports bypassing the I/O handlers, typically
3428 * the case when sending a pure TCP check which fails, then the I/O
3429 * handlers above are not called. This is completely handled by the
3430 * main processing task so let's simply wake it up. If we get here,
3431 * we expect errno to still be valid.
3432 */
3433 chk_report_conn_err(check, errno, 0);
3434 task_wakeup(check->task, TASK_WOKEN_IO);
3435 }
3436
3437 if (check->result != CHK_RES_UNKNOWN) {
3438 /* Check complete or aborted. If connection not yet closed do it
3439 * now and wake the check task up to be sure the result is
3440 * handled ASAP. */
3441 conn_sock_drain(conn);
3442 cs_close(cs);
3443 ret = -1;
3444 /* We may have been scheduled to run, and the
3445 * I/O handler expects to have a cs, so remove
3446 * the tasklet
3447 */
3448 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3449 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003450 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003451
3452 if (check->server)
3453 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003454 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003455 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003456
Christopher Faulet61cc8522020-04-20 14:54:42 +02003457 /* if a connection got replaced, we must absolutely prevent the connection
3458 * handler from touching its fd, and perform the FD polling updates ourselves
3459 */
3460 if (ret < 0)
3461 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003462
Christopher Faulet61cc8522020-04-20 14:54:42 +02003463 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003464}
3465
Christopher Faulet61cc8522020-04-20 14:54:42 +02003466/* This function checks if any I/O is wanted, and if so, attempts to do so */
3467static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003468{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003469 struct check *check = ctx;
3470 struct conn_stream *cs = check->cs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003471
Christopher Faulet3d5e1212020-05-28 14:34:02 +02003472 wake_srv_chk(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003473 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003474}
3475
Christopher Faulet61cc8522020-04-20 14:54:42 +02003476/* manages a server health-check that uses a connection. Returns
3477 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3478 *
3479 * Please do NOT place any return statement in this function and only leave
3480 * via the out_unlock label.
3481 */
3482static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003483{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003484 struct check *check = context;
3485 struct proxy *proxy = check->proxy;
3486 struct conn_stream *cs = check->cs;
3487 struct connection *conn = cs_conn(cs);
3488 int rv;
3489 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003490
Christopher Faulet61cc8522020-04-20 14:54:42 +02003491 if (check->server)
3492 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3493 if (!(check->state & CHK_ST_INPROGRESS)) {
3494 /* no check currently running */
3495 if (!expired) /* woke up too early */
3496 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003497
Christopher Faulet61cc8522020-04-20 14:54:42 +02003498 /* we don't send any health-checks when the proxy is
3499 * stopped, the server should not be checked or the check
3500 * is disabled.
3501 */
3502 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3503 proxy->state == PR_STSTOPPED)
3504 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003505
Christopher Faulet61cc8522020-04-20 14:54:42 +02003506 /* we'll initiate a new check */
3507 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003508
Christopher Faulet61cc8522020-04-20 14:54:42 +02003509 check->state |= CHK_ST_INPROGRESS;
3510 b_reset(&check->bi);
3511 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003512
Christopher Faulet61cc8522020-04-20 14:54:42 +02003513 task_set_affinity(t, tid_bit);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003514
Christopher Faulet99ff1052020-05-25 07:32:01 +02003515 check->current_step = NULL;
3516 tcpcheck_main(check);
3517 goto out_unlock;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003518 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003519 else {
3520 /* there was a test running.
3521 * First, let's check whether there was an uncaught error,
3522 * which can happen on connect timeout or error.
3523 */
3524 if (check->result == CHK_RES_UNKNOWN) {
3525 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3526 chk_report_conn_err(check, 0, expired);
3527 }
3528 else
3529 goto out_unlock; /* timeout not reached, wait again */
3530 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003531
Christopher Faulet61cc8522020-04-20 14:54:42 +02003532 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003533
Christopher Faulet61cc8522020-04-20 14:54:42 +02003534 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003535
Christopher Faulet61cc8522020-04-20 14:54:42 +02003536 if (conn && conn->xprt) {
3537 /* The check was aborted and the connection was not yet closed.
3538 * This can happen upon timeout, or when an external event such
3539 * as a failed response coupled with "observe layer7" caused the
3540 * server state to be suddenly changed.
3541 */
3542 conn_sock_drain(conn);
3543 cs_close(cs);
3544 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003545
Christopher Faulet61cc8522020-04-20 14:54:42 +02003546 if (cs) {
3547 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003548 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003549 /* We may have been scheduled to run, and the
3550 * I/O handler expects to have a cs, so remove
3551 * the tasklet
3552 */
3553 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3554 cs_destroy(cs);
3555 cs = check->cs = NULL;
3556 conn = NULL;
3557 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003558
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003559 if (check->sess != NULL) {
3560 vars_prune(&check->vars, check->sess, NULL);
3561 session_free(check->sess);
3562 check->sess = NULL;
3563 }
3564
Christopher Faulet61cc8522020-04-20 14:54:42 +02003565 if (check->server) {
3566 if (check->result == CHK_RES_FAILED) {
3567 /* a failure or timeout detected */
3568 check_notify_failure(check);
3569 }
3570 else if (check->result == CHK_RES_CONDPASS) {
3571 /* check is OK but asks for stopping mode */
3572 check_notify_stopping(check);
3573 }
3574 else if (check->result == CHK_RES_PASSED) {
3575 /* a success was detected */
3576 check_notify_success(check);
3577 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003578 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003579 task_set_affinity(t, MAX_THREADS_MASK);
3580 check->state &= ~CHK_ST_INPROGRESS;
3581
3582 if (check->server) {
3583 rv = 0;
3584 if (global.spread_checks > 0) {
3585 rv = srv_getinter(check) * global.spread_checks / 100;
3586 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3587 }
3588 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003589 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003590 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003591
Christopher Faulet61cc8522020-04-20 14:54:42 +02003592 reschedule:
3593 while (tick_is_expired(t->expire, now_ms))
3594 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3595 out_unlock:
3596 if (check->server)
3597 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3598 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003599}
3600
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003601
Christopher Faulet61cc8522020-04-20 14:54:42 +02003602/**************************************************************************/
3603/******************* Internals to parse tcp-check rules *******************/
3604/**************************************************************************/
3605struct action_kw_list tcp_check_keywords = {
3606 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3607};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003608
Christopher Faulet61cc8522020-04-20 14:54:42 +02003609/* Return the struct action_kw associated to a keyword */
3610static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003611{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003612 return action_lookup(&tcp_check_keywords.list, kw);
3613}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003614
Christopher Faulet61cc8522020-04-20 14:54:42 +02003615static void action_kw_tcp_check_build_list(struct buffer *chk)
3616{
3617 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003618}
3619
Christopher Faulet61cc8522020-04-20 14:54:42 +02003620/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3621 * returned on error.
3622 */
3623static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3624 struct list *rules, struct action_kw *kw,
3625 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003626{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003627 struct tcpcheck_rule *chk = NULL;
3628 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003629
Christopher Faulet61cc8522020-04-20 14:54:42 +02003630 actrule = calloc(1, sizeof(*actrule));
3631 if (!actrule) {
3632 memprintf(errmsg, "out of memory");
3633 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003634 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003635 actrule->kw = kw;
3636 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003637
Christopher Faulet61cc8522020-04-20 14:54:42 +02003638 cur_arg++;
3639 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3640 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3641 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003642 }
3643
Christopher Faulet61cc8522020-04-20 14:54:42 +02003644 chk = calloc(1, sizeof(*chk));
3645 if (!chk) {
3646 memprintf(errmsg, "out of memory");
3647 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003648 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003649 chk->action = TCPCHK_ACT_ACTION_KW;
3650 chk->action_kw.rule = actrule;
3651 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003652
3653 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003654 free(actrule);
3655 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003656}
3657
Christopher Faulet61cc8522020-04-20 14:54:42 +02003658/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3659 * returned on error.
3660 */
3661static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3662 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003663{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003664 struct tcpcheck_rule *chk = NULL;
3665 struct sockaddr_storage *sk = NULL;
3666 char *comment = NULL, *sni = NULL, *alpn = NULL;
3667 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003668 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003669 unsigned short conn_opts = 0;
3670 long port = 0;
3671 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003672
Christopher Faulet61cc8522020-04-20 14:54:42 +02003673 list_for_each_entry(chk, rules, list) {
3674 if (chk->action == TCPCHK_ACT_CONNECT)
3675 break;
3676 if (chk->action == TCPCHK_ACT_COMMENT ||
3677 chk->action == TCPCHK_ACT_ACTION_KW ||
3678 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3679 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003680
Christopher Faulet61cc8522020-04-20 14:54:42 +02003681 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003682 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003683 "when there is a 'connect' step in the tcp-check ruleset");
3684 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003685 }
3686
Christopher Faulet61cc8522020-04-20 14:54:42 +02003687 cur_arg++;
3688 while (*(args[cur_arg])) {
3689 if (strcmp(args[cur_arg], "default") == 0)
3690 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3691 else if (strcmp(args[cur_arg], "addr") == 0) {
3692 int port1, port2;
3693 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003694
Christopher Faulet61cc8522020-04-20 14:54:42 +02003695 if (!*(args[cur_arg+1])) {
3696 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3697 goto error;
3698 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003699
Christopher Faulet61cc8522020-04-20 14:54:42 +02003700 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3701 if (!sk) {
3702 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3703 goto error;
3704 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003705
Christopher Faulet61cc8522020-04-20 14:54:42 +02003706 proto = protocol_by_family(sk->ss_family);
3707 if (!proto || !proto->connect) {
3708 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3709 args[cur_arg]);
3710 goto error;
3711 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003712
Christopher Faulet61cc8522020-04-20 14:54:42 +02003713 if (port1 != port2) {
3714 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3715 args[cur_arg], args[cur_arg+1]);
3716 goto error;
3717 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003718
Christopher Faulet61cc8522020-04-20 14:54:42 +02003719 cur_arg++;
3720 }
3721 else if (strcmp(args[cur_arg], "port") == 0) {
3722 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003723
Christopher Faulet61cc8522020-04-20 14:54:42 +02003724 if (!*(args[cur_arg+1])) {
3725 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3726 goto error;
3727 }
3728 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003729
Christopher Faulet61cc8522020-04-20 14:54:42 +02003730 port = 0;
3731 release_sample_expr(port_expr);
3732 p = args[cur_arg]; end = p + strlen(p);
3733 port = read_uint(&p, end);
3734 if (p != end) {
3735 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003736
Christopher Faulet61cc8522020-04-20 14:54:42 +02003737 px->conf.args.ctx = ARGC_SRV;
3738 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3739 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003740
Christopher Faulet61cc8522020-04-20 14:54:42 +02003741 if (!port_expr) {
3742 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3743 goto error;
3744 }
3745 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3746 memprintf(errmsg, "error detected while parsing port expression : "
3747 " fetch method '%s' extracts information from '%s', "
3748 "none of which is available here.\n",
3749 args[cur_arg], sample_src_names(port_expr->fetch->use));
3750 goto error;
3751 }
3752 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3753 }
3754 else if (port > 65535 || port < 1) {
3755 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3756 args[cur_arg]);
3757 goto error;
3758 }
3759 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003760 else if (strcmp(args[cur_arg], "proto") == 0) {
3761 if (!*(args[cur_arg+1])) {
3762 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3763 goto error;
3764 }
3765 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3766 if (!mux_proto) {
3767 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3768 goto error;
3769 }
3770 cur_arg++;
3771 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003772 else if (strcmp(args[cur_arg], "comment") == 0) {
3773 if (!*(args[cur_arg+1])) {
3774 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3775 goto error;
3776 }
3777 cur_arg++;
3778 free(comment);
3779 comment = strdup(args[cur_arg]);
3780 if (!comment) {
3781 memprintf(errmsg, "out of memory");
3782 goto error;
3783 }
3784 }
3785 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3786 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3787 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3788 conn_opts |= TCPCHK_OPT_SOCKS4;
3789 else if (strcmp(args[cur_arg], "linger") == 0)
3790 conn_opts |= TCPCHK_OPT_LINGER;
3791#ifdef USE_OPENSSL
3792 else if (strcmp(args[cur_arg], "ssl") == 0) {
3793 px->options |= PR_O_TCPCHK_SSL;
3794 conn_opts |= TCPCHK_OPT_SSL;
3795 }
3796 else if (strcmp(args[cur_arg], "sni") == 0) {
3797 if (!*(args[cur_arg+1])) {
3798 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3799 goto error;
3800 }
3801 cur_arg++;
3802 free(sni);
3803 sni = strdup(args[cur_arg]);
3804 if (!sni) {
3805 memprintf(errmsg, "out of memory");
3806 goto error;
3807 }
3808 }
3809 else if (strcmp(args[cur_arg], "alpn") == 0) {
3810#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3811 free(alpn);
3812 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3813 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3814 goto error;
3815 }
3816 cur_arg++;
3817#else
3818 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003819 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003820#endif
3821 }
3822#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003823
Christopher Faulet61cc8522020-04-20 14:54:42 +02003824 else {
3825 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3826#ifdef USE_OPENSSL
3827 ", 'ssl', 'sni', 'alpn'"
3828#endif /* USE_OPENSSL */
3829 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3830 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003831 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003832 }
3833 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003834 }
3835
Christopher Faulet61cc8522020-04-20 14:54:42 +02003836 chk = calloc(1, sizeof(*chk));
3837 if (!chk) {
3838 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003839 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003840 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003841 chk->action = TCPCHK_ACT_CONNECT;
3842 chk->comment = comment;
3843 chk->connect.port = port;
3844 chk->connect.options = conn_opts;
3845 chk->connect.sni = sni;
3846 chk->connect.alpn = alpn;
3847 chk->connect.alpn_len= alpn_len;
3848 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003849 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003850 if (sk)
3851 chk->connect.addr = *sk;
3852 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003853
Christopher Faulet61cc8522020-04-20 14:54:42 +02003854 error:
3855 free(alpn);
3856 free(sni);
3857 free(comment);
3858 release_sample_expr(port_expr);
3859 return NULL;
3860}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003861
Christopher Faulet61cc8522020-04-20 14:54:42 +02003862/* Parses and creates a tcp-check send rule. NULL is returned on error */
3863static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3864 const char *file, int line, char **errmsg)
3865{
3866 struct tcpcheck_rule *chk = NULL;
3867 char *comment = NULL, *data = NULL;
3868 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003869
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003870 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3871 type = TCPCHK_SEND_BINARY_LF;
3872 else if (strcmp(args[cur_arg], "send-binary") == 0)
3873 type = TCPCHK_SEND_BINARY;
3874 else if (strcmp(args[cur_arg], "send-lf") == 0)
3875 type = TCPCHK_SEND_STRING_LF;
3876 else if (strcmp(args[cur_arg], "send") == 0)
3877 type = TCPCHK_SEND_STRING;
3878
Christopher Faulet61cc8522020-04-20 14:54:42 +02003879 if (!*(args[cur_arg+1])) {
3880 memprintf(errmsg, "'%s' expects a %s as argument",
3881 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003882 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003883 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003884
Christopher Faulet61cc8522020-04-20 14:54:42 +02003885 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003886
Christopher Faulet61cc8522020-04-20 14:54:42 +02003887 cur_arg += 2;
3888 while (*(args[cur_arg])) {
3889 if (strcmp(args[cur_arg], "comment") == 0) {
3890 if (!*(args[cur_arg+1])) {
3891 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3892 goto error;
3893 }
3894 cur_arg++;
3895 free(comment);
3896 comment = strdup(args[cur_arg]);
3897 if (!comment) {
3898 memprintf(errmsg, "out of memory");
3899 goto error;
3900 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003901 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003902 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003903 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003904 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003905 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003906 }
3907 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003908 }
3909
Christopher Faulet61cc8522020-04-20 14:54:42 +02003910 chk = calloc(1, sizeof(*chk));
3911 if (!chk) {
3912 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003913 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003914 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003915 chk->action = TCPCHK_ACT_SEND;
3916 chk->comment = comment;
3917 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003918
Christopher Faulet61cc8522020-04-20 14:54:42 +02003919 switch (chk->send.type) {
3920 case TCPCHK_SEND_STRING:
3921 chk->send.data = ist2(strdup(data), strlen(data));
3922 if (!isttest(chk->send.data)) {
3923 memprintf(errmsg, "out of memory");
3924 goto error;
3925 }
3926 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003927 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003928 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003929 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003930 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3931 goto error;
3932 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003933 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003934 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003935 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003936 case TCPCHK_SEND_STRING_LF:
3937 case TCPCHK_SEND_BINARY_LF:
3938 LIST_INIT(&chk->send.fmt);
3939 px->conf.args.ctx = ARGC_SRV;
3940 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3941 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3942 goto error;
3943 }
3944 break;
3945 case TCPCHK_SEND_HTTP:
3946 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003947 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003948 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003949
Christopher Faulet61cc8522020-04-20 14:54:42 +02003950 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003951
Christopher Faulet61cc8522020-04-20 14:54:42 +02003952 error:
3953 free(chk);
3954 free(comment);
3955 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003956}
3957
Christopher Faulet61cc8522020-04-20 14:54:42 +02003958/* Parses and creates a http-check send rule. NULL is returned on error */
3959static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3960 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003961{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003962 struct tcpcheck_rule *chk = NULL;
3963 struct tcpcheck_http_hdr *hdr = NULL;
3964 struct http_hdr hdrs[global.tune.max_http_hdr];
3965 char *meth = NULL, *uri = NULL, *vsn = NULL;
3966 char *body = NULL, *comment = NULL;
3967 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003968 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003969
3970 cur_arg++;
3971 while (*(args[cur_arg])) {
3972 if (strcmp(args[cur_arg], "meth") == 0) {
3973 if (!*(args[cur_arg+1])) {
3974 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3975 goto error;
3976 }
3977 cur_arg++;
3978 meth = args[cur_arg];
3979 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003980 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003981 if (!*(args[cur_arg+1])) {
3982 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3983 goto error;
3984 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003985 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
3986 if (strcmp(args[cur_arg], "uri-lf") == 0)
3987 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003988 cur_arg++;
3989 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02003990 }
Christopher Faulet907701b2020-04-28 09:37:00 +02003991 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003992 if (!*(args[cur_arg+1])) {
3993 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3994 goto error;
3995 }
3996 cur_arg++;
3997 vsn = args[cur_arg];
3998 }
3999 else if (strcmp(args[cur_arg], "hdr") == 0) {
4000 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4001 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4002 goto error;
4003 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004004
4005 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4006 if (host_hdr >= 0) {
4007 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4008 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4009 goto error;
4010 }
4011 host_hdr = i;
4012 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004013 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4014 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4015 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4016 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004017
Christopher Faulet61cc8522020-04-20 14:54:42 +02004018 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4019 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4020 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004021 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004022 cur_arg += 2;
4023 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004024 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004025 if (!*(args[cur_arg+1])) {
4026 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4027 goto error;
4028 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004029 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4030 if (strcmp(args[cur_arg], "body-lf") == 0)
4031 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004032 cur_arg++;
4033 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004034 }
4035 else if (strcmp(args[cur_arg], "comment") == 0) {
4036 if (!*(args[cur_arg+1])) {
4037 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4038 goto error;
4039 }
4040 cur_arg++;
4041 free(comment);
4042 comment = strdup(args[cur_arg]);
4043 if (!comment) {
4044 memprintf(errmsg, "out of memory");
4045 goto error;
4046 }
4047 }
4048 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004049 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004050 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004051 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004052 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004053 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004054 }
4055
Christopher Faulet61cc8522020-04-20 14:54:42 +02004056 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004057
Christopher Faulet61cc8522020-04-20 14:54:42 +02004058 chk = calloc(1, sizeof(*chk));
4059 if (!chk) {
4060 memprintf(errmsg, "out of memory");
4061 goto error;
4062 }
4063 chk->action = TCPCHK_ACT_SEND;
4064 chk->comment = comment; comment = NULL;
4065 chk->send.type = TCPCHK_SEND_HTTP;
4066 chk->send.http.flags = flags;
4067 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004068
Christopher Faulet61cc8522020-04-20 14:54:42 +02004069 if (meth) {
4070 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4071 chk->send.http.meth.str.area = strdup(meth);
4072 chk->send.http.meth.str.data = strlen(meth);
4073 if (!chk->send.http.meth.str.area) {
4074 memprintf(errmsg, "out of memory");
4075 goto error;
4076 }
4077 }
4078 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004079 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4080 LIST_INIT(&chk->send.http.uri_fmt);
4081 px->conf.args.ctx = ARGC_SRV;
4082 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4083 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4084 goto error;
4085 }
4086 }
4087 else {
4088 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4089 if (!isttest(chk->send.http.uri)) {
4090 memprintf(errmsg, "out of memory");
4091 goto error;
4092 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004093 }
4094 }
4095 if (vsn) {
4096 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4097 if (!isttest(chk->send.http.vsn)) {
4098 memprintf(errmsg, "out of memory");
4099 goto error;
4100 }
4101 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004102 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004103 hdr = calloc(1, sizeof(*hdr));
4104 if (!hdr) {
4105 memprintf(errmsg, "out of memory");
4106 goto error;
4107 }
4108 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004109 hdr->name = istdup(hdrs[i].n);
4110 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004111 memprintf(errmsg, "out of memory");
4112 goto error;
4113 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004114
Christopher Fauletb61caf42020-04-21 10:57:42 +02004115 ist0(hdrs[i].v);
4116 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 +02004117 goto error;
4118 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4119 hdr = NULL;
4120 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004121
Christopher Faulet61cc8522020-04-20 14:54:42 +02004122 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004123 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4124 LIST_INIT(&chk->send.http.body_fmt);
4125 px->conf.args.ctx = ARGC_SRV;
4126 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4127 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4128 goto error;
4129 }
4130 }
4131 else {
4132 chk->send.http.body = ist2(strdup(body), strlen(body));
4133 if (!isttest(chk->send.http.body)) {
4134 memprintf(errmsg, "out of memory");
4135 goto error;
4136 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004137 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004138 }
4139
Christopher Faulet61cc8522020-04-20 14:54:42 +02004140 return chk;
4141
4142 error:
4143 free_tcpcheck_http_hdr(hdr);
4144 free_tcpcheck(chk, 0);
4145 free(comment);
4146 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004147}
4148
Christopher Faulet61cc8522020-04-20 14:54:42 +02004149/* Parses and creates a http-check comment rule. NULL is returned on error */
4150static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4151 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004152{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004153 struct tcpcheck_rule *chk = NULL;
4154 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004155
Christopher Faulet61cc8522020-04-20 14:54:42 +02004156 if (!*(args[cur_arg+1])) {
4157 memprintf(errmsg, "expects a string as argument");
4158 goto error;
4159 }
4160 cur_arg++;
4161 comment = strdup(args[cur_arg]);
4162 if (!comment) {
4163 memprintf(errmsg, "out of memory");
4164 goto error;
4165 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004166
Christopher Faulet61cc8522020-04-20 14:54:42 +02004167 chk = calloc(1, sizeof(*chk));
4168 if (!chk) {
4169 memprintf(errmsg, "out of memory");
4170 goto error;
4171 }
4172 chk->action = TCPCHK_ACT_COMMENT;
4173 chk->comment = comment;
4174 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004175
Christopher Faulet61cc8522020-04-20 14:54:42 +02004176 error:
4177 free(comment);
4178 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004179}
4180
Christopher Faulet61cc8522020-04-20 14:54:42 +02004181/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4182 * on error. <proto> is set to the right protocol flags (covered by the
4183 * TCPCHK_RULES_PROTO_CHK mask).
4184 */
4185static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4186 struct list *rules, unsigned int proto,
4187 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004188{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004189 struct tcpcheck_rule *prev_check, *chk = NULL;
4190 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004191 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004192 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004193 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4194 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4195 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004196 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004197 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004198 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004199
Christopher Faulet39708192020-05-05 10:47:36 +02004200 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004201 if (!*(args[cur_arg+1])) {
4202 memprintf(errmsg, "expects at least a matching pattern as arguments");
4203 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004204 }
4205
Christopher Faulet61cc8522020-04-20 14:54:42 +02004206 cur_arg++;
4207 while (*(args[cur_arg])) {
4208 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004209
Christopher Faulet61cc8522020-04-20 14:54:42 +02004210 rescan:
4211 if (strcmp(args[cur_arg], "min-recv") == 0) {
4212 if (in_pattern) {
4213 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4214 goto error;
4215 }
4216 if (!*(args[cur_arg+1])) {
4217 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4218 goto error;
4219 }
4220 /* Use an signed integer here because of chksize */
4221 cur_arg++;
4222 min_recv = atol(args[cur_arg]);
4223 if (min_recv < -1 || min_recv > INT_MAX) {
4224 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4225 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004226 }
4227 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004228 else if (*(args[cur_arg]) == '!') {
4229 in_pattern = 1;
4230 while (*(args[cur_arg]) == '!') {
4231 inverse = !inverse;
4232 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004233 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004234 if (!*(args[cur_arg]))
4235 cur_arg++;
4236 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004237 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004238 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4239 if (type != TCPCHK_EXPECT_UNDEF) {
4240 memprintf(errmsg, "only on pattern expected");
4241 goto error;
4242 }
4243 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004244 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004245 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004246 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004247
Christopher Faulet61cc8522020-04-20 14:54:42 +02004248 if (!*(args[cur_arg+1])) {
4249 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4250 goto error;
4251 }
4252 cur_arg++;
4253 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004254 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004255 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4256 if (proto == TCPCHK_RULES_HTTP_CHK)
4257 goto bad_http_kw;
4258 if (type != TCPCHK_EXPECT_UNDEF) {
4259 memprintf(errmsg, "only on pattern expected");
4260 goto error;
4261 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004262 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004263
Christopher Faulet61cc8522020-04-20 14:54:42 +02004264 if (!*(args[cur_arg+1])) {
4265 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4266 goto error;
4267 }
4268 cur_arg++;
4269 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004270 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004271 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4272 if (type != TCPCHK_EXPECT_UNDEF) {
4273 memprintf(errmsg, "only on pattern expected");
4274 goto error;
4275 }
4276 if (proto != TCPCHK_RULES_HTTP_CHK)
4277 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4278 else {
4279 if (*(args[cur_arg]) != 's')
4280 goto bad_http_kw;
4281 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4282 }
4283
4284 if (!*(args[cur_arg+1])) {
4285 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4286 goto error;
4287 }
4288 cur_arg++;
4289 pattern = args[cur_arg];
4290 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004291 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4292 if (proto != TCPCHK_RULES_HTTP_CHK)
4293 goto bad_tcp_kw;
4294 if (type != TCPCHK_EXPECT_UNDEF) {
4295 memprintf(errmsg, "only on pattern expected");
4296 goto error;
4297 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004298 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004299
Christopher Faulet61cc8522020-04-20 14:54:42 +02004300 if (!*(args[cur_arg+1])) {
4301 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4302 goto error;
4303 }
4304 cur_arg++;
4305 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004306 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004307 else if (strcmp(args[cur_arg], "custom") == 0) {
4308 if (in_pattern) {
4309 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4310 goto error;
4311 }
4312 if (type != TCPCHK_EXPECT_UNDEF) {
4313 memprintf(errmsg, "only on pattern expected");
4314 goto error;
4315 }
4316 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004317 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004318 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004319 int orig_arg = cur_arg;
4320
4321 if (proto != TCPCHK_RULES_HTTP_CHK)
4322 goto bad_tcp_kw;
4323 if (type != TCPCHK_EXPECT_UNDEF) {
4324 memprintf(errmsg, "only on pattern expected");
4325 goto error;
4326 }
4327 type = TCPCHK_EXPECT_HTTP_HEADER;
4328
Christopher Fauletb5594262020-05-05 20:23:13 +02004329 if (strcmp(args[cur_arg], "fhdr") == 0)
4330 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4331
Christopher Faulet39708192020-05-05 10:47:36 +02004332 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004333 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4334 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4335 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004336 args[orig_arg]);
4337 goto error;
4338 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004339
4340 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4341 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4342
Christopher Faulet39708192020-05-05 10:47:36 +02004343 cur_arg += 2;
4344 if (strcmp(args[cur_arg], "-m") == 0) {
4345 if (!*(args[cur_arg+1])) {
4346 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4347 args[orig_arg], args[cur_arg]);
4348 goto error;
4349 }
4350 if (strcmp(args[cur_arg+1], "str") == 0)
4351 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4352 else if (strcmp(args[cur_arg+1], "beg") == 0)
4353 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4354 else if (strcmp(args[cur_arg+1], "end") == 0)
4355 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4356 else if (strcmp(args[cur_arg+1], "sub") == 0)
4357 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004358 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4359 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4360 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4361 args[orig_arg]);
4362 goto error;
4363 }
Christopher Faulet39708192020-05-05 10:47:36 +02004364 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004365 }
Christopher Faulet39708192020-05-05 10:47:36 +02004366 else {
4367 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4368 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4369 goto error;
4370 }
4371 cur_arg += 2;
4372 }
4373 else
4374 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4375 npat = args[cur_arg];
4376
Christopher Fauletb5594262020-05-05 20:23:13 +02004377 if (!*(args[cur_arg+1]) ||
4378 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004379 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4380 goto next;
4381 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004382 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4383 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004384
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004385 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004386 if (strcmp(args[cur_arg+2], "-m") == 0) {
4387 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004388 if (!*(args[cur_arg+1])) {
4389 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4390 args[orig_arg], args[cur_arg]);
4391 goto error;
4392 }
4393 if (strcmp(args[cur_arg+1], "str") == 0)
4394 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4395 else if (strcmp(args[cur_arg+1], "beg") == 0)
4396 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4397 else if (strcmp(args[cur_arg+1], "end") == 0)
4398 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4399 else if (strcmp(args[cur_arg+1], "sub") == 0)
4400 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004401 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4402 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4403 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4404 args[orig_arg]);
4405 goto error;
4406 }
Christopher Faulet39708192020-05-05 10:47:36 +02004407 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004408 }
Christopher Faulet39708192020-05-05 10:47:36 +02004409 else {
4410 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4411 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4412 goto error;
4413 }
Christopher Faulet39708192020-05-05 10:47:36 +02004414 }
4415 else
4416 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004417
Christopher Fauletb5594262020-05-05 20:23:13 +02004418 if (!*(args[cur_arg+2])) {
4419 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4420 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004421 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004422 vpat = args[cur_arg+2];
4423 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004424 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004425 else if (strcmp(args[cur_arg], "comment") == 0) {
4426 if (in_pattern) {
4427 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4428 goto error;
4429 }
4430 if (!*(args[cur_arg+1])) {
4431 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4432 goto error;
4433 }
4434 cur_arg++;
4435 free(comment);
4436 comment = strdup(args[cur_arg]);
4437 if (!comment) {
4438 memprintf(errmsg, "out of memory");
4439 goto error;
4440 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004441 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004442 else if (strcmp(args[cur_arg], "on-success") == 0) {
4443 if (in_pattern) {
4444 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4445 goto error;
4446 }
4447 if (!*(args[cur_arg+1])) {
4448 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4449 goto error;
4450 }
4451 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004452 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004453 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004454 else if (strcmp(args[cur_arg], "on-error") == 0) {
4455 if (in_pattern) {
4456 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4457 goto error;
4458 }
4459 if (!*(args[cur_arg+1])) {
4460 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4461 goto error;
4462 }
4463 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004464 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004465 }
4466 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4467 if (in_pattern) {
4468 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4469 goto error;
4470 }
4471 if (!*(args[cur_arg+1])) {
4472 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4473 goto error;
4474 }
4475 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4476 ok_st = HCHK_STATUS_L7OKD;
4477 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4478 ok_st = HCHK_STATUS_L7OKCD;
4479 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4480 ok_st = HCHK_STATUS_L6OK;
4481 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4482 ok_st = HCHK_STATUS_L4OK;
4483 else {
4484 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4485 args[cur_arg], args[cur_arg+1]);
4486 goto error;
4487 }
4488 cur_arg++;
4489 }
4490 else if (strcmp(args[cur_arg], "error-status") == 0) {
4491 if (in_pattern) {
4492 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4493 goto error;
4494 }
4495 if (!*(args[cur_arg+1])) {
4496 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4497 goto error;
4498 }
4499 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4500 err_st = HCHK_STATUS_L7RSP;
4501 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4502 err_st = HCHK_STATUS_L7STS;
4503 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4504 err_st = HCHK_STATUS_L6RSP;
4505 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4506 err_st = HCHK_STATUS_L4CON;
4507 else {
4508 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4509 args[cur_arg], args[cur_arg+1]);
4510 goto error;
4511 }
4512 cur_arg++;
4513 }
4514 else if (strcmp(args[cur_arg], "status-code") == 0) {
4515 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004516
Christopher Faulet61cc8522020-04-20 14:54:42 +02004517 if (in_pattern) {
4518 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4519 goto error;
4520 }
4521 if (!*(args[cur_arg+1])) {
4522 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4523 goto error;
4524 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004525
Christopher Faulet61cc8522020-04-20 14:54:42 +02004526 cur_arg++;
4527 release_sample_expr(status_expr);
4528 px->conf.args.ctx = ARGC_SRV;
4529 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4530 file, line, errmsg, &px->conf.args, NULL);
4531 if (!status_expr) {
4532 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4533 goto error;
4534 }
4535 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4536 memprintf(errmsg, "error detected while parsing status-code expression : "
4537 " fetch method '%s' extracts information from '%s', "
4538 "none of which is available here.\n",
4539 args[cur_arg], sample_src_names(status_expr->fetch->use));
4540 goto error;
4541 }
4542 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4543 }
4544 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4545 if (in_pattern) {
4546 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4547 goto error;
4548 }
4549 if (!*(args[cur_arg+1])) {
4550 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4551 goto error;
4552 }
4553 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4554 tout_st = HCHK_STATUS_L7TOUT;
4555 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4556 tout_st = HCHK_STATUS_L6TOUT;
4557 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4558 tout_st = HCHK_STATUS_L4TOUT;
4559 else {
4560 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4561 args[cur_arg], args[cur_arg+1]);
4562 goto error;
4563 }
4564 cur_arg++;
4565 }
4566 else {
4567 if (proto == TCPCHK_RULES_HTTP_CHK) {
4568 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004569 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004570 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004571 }
4572 else {
4573 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004574 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4575 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004576 }
4577 goto error;
4578 }
Christopher Faulet39708192020-05-05 10:47:36 +02004579 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004580 cur_arg++;
4581 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004582
Christopher Faulet61cc8522020-04-20 14:54:42 +02004583 chk = calloc(1, sizeof(*chk));
4584 if (!chk) {
4585 memprintf(errmsg, "out of memory");
4586 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004587 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004588 chk->action = TCPCHK_ACT_EXPECT;
4589 LIST_INIT(&chk->expect.onerror_fmt);
4590 LIST_INIT(&chk->expect.onsuccess_fmt);
4591 chk->comment = comment; comment = NULL;
4592 chk->expect.type = type;
4593 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004594 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004595 chk->expect.ok_status = ok_st;
4596 chk->expect.err_status = err_st;
4597 chk->expect.tout_status = tout_st;
4598 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004599
Christopher Faulet61cc8522020-04-20 14:54:42 +02004600 if (on_success_msg) {
4601 px->conf.args.ctx = ARGC_SRV;
4602 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4603 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4604 goto error;
4605 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004606 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004607 if (on_error_msg) {
4608 px->conf.args.ctx = ARGC_SRV;
4609 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4610 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4611 goto error;
4612 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004613 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004614
Christopher Faulet61cc8522020-04-20 14:54:42 +02004615 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004616 case TCPCHK_EXPECT_HTTP_STATUS: {
4617 const char *p = pattern;
4618 unsigned int c1,c2;
4619
4620 chk->expect.codes.codes = NULL;
4621 chk->expect.codes.num = 0;
4622 while (1) {
4623 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4624 if (*p == '-') {
4625 p++;
4626 c2 = read_uint(&p, pattern + strlen(pattern));
4627 }
4628 if (c1 > c2) {
4629 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4630 goto error;
4631 }
4632
4633 chk->expect.codes.num++;
4634 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4635 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4636 if (!chk->expect.codes.codes) {
4637 memprintf(errmsg, "out of memory");
4638 goto error;
4639 }
4640 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4641 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4642
4643 if (*p == '\0')
4644 break;
4645 if (*p != ',') {
4646 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4647 goto error;
4648 }
4649 p++;
4650 }
4651 break;
4652 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004653 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004654 case TCPCHK_EXPECT_HTTP_BODY:
4655 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004656 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004657 memprintf(errmsg, "out of memory");
4658 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004659 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004660 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004661 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004662 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004663
4664 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004665 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4666 goto error;
4667 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004668 chk->expect.data.len = len;
4669 break;
4670 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004671 case TCPCHK_EXPECT_STRING_REGEX:
4672 case TCPCHK_EXPECT_BINARY_REGEX:
4673 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4674 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004675 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004676 if (!chk->expect.regex)
4677 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004678 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004679
4680 case TCPCHK_EXPECT_STRING_LF:
4681 case TCPCHK_EXPECT_BINARY_LF:
4682 case TCPCHK_EXPECT_HTTP_BODY_LF:
4683 LIST_INIT(&chk->expect.fmt);
4684 px->conf.args.ctx = ARGC_SRV;
4685 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4686 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4687 goto error;
4688 }
4689 break;
4690
Christopher Faulet39708192020-05-05 10:47:36 +02004691 case TCPCHK_EXPECT_HTTP_HEADER:
4692 if (!npat) {
4693 memprintf(errmsg, "unexpected error, undefined header name pattern");
4694 goto error;
4695 }
4696 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4697 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4698 if (!chk->expect.hdr.name_re)
4699 goto error;
4700 }
4701 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4702 px->conf.args.ctx = ARGC_SRV;
4703 LIST_INIT(&chk->expect.hdr.name_fmt);
4704 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4705 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4706 goto error;
4707 }
4708 }
4709 else {
4710 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4711 if (!isttest(chk->expect.hdr.name)) {
4712 memprintf(errmsg, "out of memory");
4713 goto error;
4714 }
4715 }
4716
4717 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4718 chk->expect.hdr.value = IST_NULL;
4719 break;
4720 }
4721
4722 if (!vpat) {
4723 memprintf(errmsg, "unexpected error, undefined header value pattern");
4724 goto error;
4725 }
4726 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4727 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4728 if (!chk->expect.hdr.value_re)
4729 goto error;
4730 }
4731 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4732 px->conf.args.ctx = ARGC_SRV;
4733 LIST_INIT(&chk->expect.hdr.value_fmt);
4734 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4735 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4736 goto error;
4737 }
4738 }
4739 else {
4740 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4741 if (!isttest(chk->expect.hdr.value)) {
4742 memprintf(errmsg, "out of memory");
4743 goto error;
4744 }
4745 }
4746
Christopher Faulet61cc8522020-04-20 14:54:42 +02004747 break;
4748 case TCPCHK_EXPECT_CUSTOM:
4749 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4750 break;
4751 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004752 memprintf(errmsg, "pattern not found");
4753 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004754 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004755
Christopher Faulet61cc8522020-04-20 14:54:42 +02004756 /* All tcp-check expect points back to the first inverse expect rule in
4757 * a chain of one or more expect rule, potentially itself.
4758 */
4759 chk->expect.head = chk;
4760 list_for_each_entry_rev(prev_check, rules, list) {
4761 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4762 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4763 chk->expect.head = prev_check;
4764 continue;
4765 }
4766 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4767 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004768 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004769 return chk;
4770
4771 error:
4772 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004773 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004774 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004775 return NULL;
4776}
4777
Christopher Faulet61cc8522020-04-20 14:54:42 +02004778/* Overwrites fields of the old http send rule with those of the new one. When
4779 * replaced, old values are freed and replaced by the new ones. New values are
4780 * not copied but transferred. At the end <new> should be empty and can be
4781 * safely released. This function never fails.
4782 */
4783static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004784{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004785 struct logformat_node *lf, *lfb;
4786 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004787
Christopher Faulet404f9192020-04-09 23:13:54 +02004788
Christopher Faulet61cc8522020-04-20 14:54:42 +02004789 if (new->send.http.meth.str.area) {
4790 free(old->send.http.meth.str.area);
4791 old->send.http.meth.meth = new->send.http.meth.meth;
4792 old->send.http.meth.str.area = new->send.http.meth.str.area;
4793 old->send.http.meth.str.data = new->send.http.meth.str.data;
4794 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004795 }
4796
Christopher Faulet61cc8522020-04-20 14:54:42 +02004797 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4798 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004799 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004800 else
4801 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4802 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4803 old->send.http.uri = new->send.http.uri;
4804 new->send.http.uri = IST_NULL;
4805 }
4806 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4807 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004808 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004809 else
4810 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4811 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4812 LIST_INIT(&old->send.http.uri_fmt);
4813 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4814 LIST_DEL(&lf->list);
4815 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4816 }
4817 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004818
Christopher Faulet61cc8522020-04-20 14:54:42 +02004819 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004820 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004821 old->send.http.vsn = new->send.http.vsn;
4822 new->send.http.vsn = IST_NULL;
4823 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004824
Christopher Faulet61cc8522020-04-20 14:54:42 +02004825 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4826 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4827 LIST_DEL(&hdr->list);
4828 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004829 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004830
4831 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4832 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004833 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004834 else
4835 free_tcpcheck_fmt(&old->send.http.body_fmt);
4836 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4837 old->send.http.body = new->send.http.body;
4838 new->send.http.body = IST_NULL;
4839 }
4840 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4841 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004842 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004843 else
4844 free_tcpcheck_fmt(&old->send.http.body_fmt);
4845 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4846 LIST_INIT(&old->send.http.body_fmt);
4847 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4848 LIST_DEL(&lf->list);
4849 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4850 }
4851 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004852}
4853
Christopher Faulet61cc8522020-04-20 14:54:42 +02004854/* Internal function used to add an http-check rule in a list during the config
4855 * parsing step. Depending on its type, and the previously inserted rules, a
4856 * specific action may be performed or an error may be reported. This functions
4857 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4858 * message.
4859 */
4860static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004861{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004862 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004863
Christopher Faulet61cc8522020-04-20 14:54:42 +02004864 /* the implicit send rule coming from an "option httpchk" line must be
4865 * merged with the first explici http-check send rule, if
4866 * any. Depdending the declaration order some tests are required.
4867 *
4868 * Some tests is also required for other kinds of http-check rules to be
4869 * sure the ruleset remains valid.
4870 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004871
Christopher Faulet61cc8522020-04-20 14:54:42 +02004872 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004873 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004874 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4875 * following tests are performed :
4876 *
4877 * 1- If there is no such rule or if it is not a send rule, the implicit send
4878 * rule is pushed in front of the ruleset
4879 *
4880 * 2- If it is another implicit send rule, it is replaced with the new one.
4881 *
4882 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004883 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004884 * new send rule (the implicit one).
4885 */
4886 r = get_first_tcpcheck_rule(rules);
4887 if (r && r->action == TCPCHK_ACT_CONNECT)
4888 r = get_next_tcpcheck_rule(rules, r);
4889 if (!r || r->action != TCPCHK_ACT_SEND)
4890 LIST_ADD(rules->list, &chk->list);
4891 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4892 LIST_DEL(&r->list);
4893 free_tcpcheck(r, 0);
4894 LIST_ADD(rules->list, &chk->list);
4895 }
4896 else {
4897 tcpcheck_overwrite_send_http_rule(r, chk);
4898 free_tcpcheck(chk, 0);
4899 }
4900 }
4901 else {
4902 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4903 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4904 * with an existing implicit send rule, if any. At the end, if there is no error,
4905 * the rule is appended to the list.
4906 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004907
Christopher Faulet61cc8522020-04-20 14:54:42 +02004908 r = get_last_tcpcheck_rule(rules);
4909 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4910 /* no error */;
4911 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4912 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4913 chk->index+1);
4914 return 0;
4915 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004916 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004917 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4918 chk->index+1);
4919 return 0;
4920 }
4921 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4922 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4923 chk->index+1);
4924 return 0;
4925 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004926
Christopher Faulet61cc8522020-04-20 14:54:42 +02004927 if (chk->action == TCPCHK_ACT_SEND) {
4928 r = get_first_tcpcheck_rule(rules);
4929 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4930 tcpcheck_overwrite_send_http_rule(r, chk);
4931 free_tcpcheck(chk, 0);
4932 LIST_DEL(&r->list);
4933 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4934 chk = r;
4935 }
4936 }
4937 LIST_ADDQ(rules->list, &chk->list);
4938 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004939 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004940}
4941
Christopher Faulet61cc8522020-04-20 14:54:42 +02004942/**************************************************************************/
4943/************************** Init/deinit checks ****************************/
4944/**************************************************************************/
4945static const char *init_check(struct check *check, int type)
4946{
4947 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004948
Christopher Faulet61cc8522020-04-20 14:54:42 +02004949 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4950 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004951
Christopher Faulet61cc8522020-04-20 14:54:42 +02004952 check->bi.area = calloc(check->bi.size, sizeof(char));
4953 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004954
Christopher Faulet61cc8522020-04-20 14:54:42 +02004955 if (!check->bi.area || !check->bo.area)
4956 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004957
Christopher Faulet61cc8522020-04-20 14:54:42 +02004958 check->wait_list.tasklet = tasklet_new();
4959 if (!check->wait_list.tasklet)
4960 return "out of memory while allocating check tasklet";
4961 check->wait_list.events = 0;
4962 check->wait_list.tasklet->process = event_srv_chk_io;
4963 check->wait_list.tasklet->context = check;
4964 return NULL;
4965}
4966
4967void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004968{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004969 task_destroy(check->task);
4970 if (check->wait_list.tasklet)
4971 tasklet_free(check->wait_list.tasklet);
4972
4973 free(check->bi.area);
4974 free(check->bo.area);
4975 if (check->cs) {
4976 free(check->cs->conn);
4977 check->cs->conn = NULL;
4978 cs_free(check->cs);
4979 check->cs = NULL;
4980 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004981}
4982
Christopher Faulet61cc8522020-04-20 14:54:42 +02004983/* manages a server health-check. Returns the time the task accepts to wait, or
4984 * TIME_ETERNITY for infinity.
4985 */
4986static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004987{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004988 struct check *check = context;
4989
4990 if (check->type == PR_O2_EXT_CHK)
4991 return process_chk_proc(t, context, state);
4992 return process_chk_conn(t, context, state);
4993
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004994}
4995
Christopher Faulet61cc8522020-04-20 14:54:42 +02004996
4997static int start_check_task(struct check *check, int mininter,
4998 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004999{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005000 struct task *t;
5001 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005002
Christopher Faulet61cc8522020-04-20 14:54:42 +02005003 if (check->type == PR_O2_EXT_CHK)
5004 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005005
Christopher Faulet61cc8522020-04-20 14:54:42 +02005006 /* task for the check */
5007 if ((t = task_new(thread_mask)) == NULL) {
5008 ha_alert("Starting [%s:%s] check: out of memory.\n",
5009 check->server->proxy->id, check->server->id);
5010 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005011 }
5012
Christopher Faulet61cc8522020-04-20 14:54:42 +02005013 check->task = t;
5014 t->process = process_chk;
5015 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005016
Christopher Faulet61cc8522020-04-20 14:54:42 +02005017 if (mininter < srv_getinter(check))
5018 mininter = srv_getinter(check);
5019
5020 if (global.max_spread_checks && mininter > global.max_spread_checks)
5021 mininter = global.max_spread_checks;
5022
5023 /* check this every ms */
5024 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5025 check->start = now;
5026 task_queue(t);
5027
5028 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005029}
5030
Christopher Faulet61cc8522020-04-20 14:54:42 +02005031/* updates the server's weight during a warmup stage. Once the final weight is
5032 * reached, the task automatically stops. Note that any server status change
5033 * must have updated s->last_change accordingly.
5034 */
5035static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005036{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005037 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005038
Christopher Faulet61cc8522020-04-20 14:54:42 +02005039 /* by default, plan on stopping the task */
5040 t->expire = TICK_ETERNITY;
5041 if ((s->next_admin & SRV_ADMF_MAINT) ||
5042 (s->next_state != SRV_ST_STARTING))
5043 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005044
Christopher Faulet61cc8522020-04-20 14:54:42 +02005045 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005046
Christopher Faulet61cc8522020-04-20 14:54:42 +02005047 /* recalculate the weights and update the state */
5048 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005049
Christopher Faulet61cc8522020-04-20 14:54:42 +02005050 /* probably that we can refill this server with a bit more connections */
5051 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005052
Christopher Faulet61cc8522020-04-20 14:54:42 +02005053 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005054
Christopher Faulet61cc8522020-04-20 14:54:42 +02005055 /* get back there in 1 second or 1/20th of the slowstart interval,
5056 * whichever is greater, resulting in small 5% steps.
5057 */
5058 if (s->next_state == SRV_ST_STARTING)
5059 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5060 return t;
5061}
5062
5063/*
5064 * Start health-check.
5065 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5066 */
5067static int start_checks()
5068{
5069
5070 struct proxy *px;
5071 struct server *s;
5072 struct task *t;
5073 int nbcheck=0, mininter=0, srvpos=0;
5074
5075 /* 0- init the dummy frontend used to create all checks sessions */
5076 init_new_proxy(&checks_fe);
5077 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5078 checks_fe.mode = PR_MODE_TCP;
5079 checks_fe.maxconn = 0;
5080 checks_fe.conn_retries = CONN_RETRIES;
5081 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5082 checks_fe.timeout.client = TICK_ETERNITY;
5083
5084 /* 1- count the checkers to run simultaneously.
5085 * We also determine the minimum interval among all of those which
5086 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5087 * will be used to spread their start-up date. Those which have
5088 * a shorter interval will start independently and will not dictate
5089 * too short an interval for all others.
5090 */
5091 for (px = proxies_list; px; px = px->next) {
5092 for (s = px->srv; s; s = s->next) {
5093 if (s->slowstart) {
5094 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5095 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5096 return ERR_ALERT | ERR_FATAL;
5097 }
5098 /* We need a warmup task that will be called when the server
5099 * state switches from down to up.
5100 */
5101 s->warmup = t;
5102 t->process = server_warmup;
5103 t->context = s;
5104 /* server can be in this state only because of */
5105 if (s->next_state == SRV_ST_STARTING)
5106 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 +02005107 }
5108
Christopher Faulet61cc8522020-04-20 14:54:42 +02005109 if (s->check.state & CHK_ST_CONFIGURED) {
5110 nbcheck++;
5111 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5112 (!mininter || mininter > srv_getinter(&s->check)))
5113 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005114 }
5115
Christopher Faulet61cc8522020-04-20 14:54:42 +02005116 if (s->agent.state & CHK_ST_CONFIGURED) {
5117 nbcheck++;
5118 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5119 (!mininter || mininter > srv_getinter(&s->agent)))
5120 mininter = srv_getinter(&s->agent);
5121 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005122 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005123 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005124
Christopher Faulet61cc8522020-04-20 14:54:42 +02005125 if (!nbcheck)
5126 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005127
Christopher Faulet61cc8522020-04-20 14:54:42 +02005128 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005129
Christopher Faulet61cc8522020-04-20 14:54:42 +02005130 /*
5131 * 2- start them as far as possible from each others. For this, we will
5132 * start them after their interval set to the min interval divided by
5133 * the number of servers, weighted by the server's position in the list.
5134 */
5135 for (px = proxies_list; px; px = px->next) {
5136 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5137 if (init_pid_list()) {
5138 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5139 return ERR_ALERT | ERR_FATAL;
5140 }
5141 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005142
Christopher Faulet61cc8522020-04-20 14:54:42 +02005143 for (s = px->srv; s; s = s->next) {
5144 /* A task for the main check */
5145 if (s->check.state & CHK_ST_CONFIGURED) {
5146 if (s->check.type == PR_O2_EXT_CHK) {
5147 if (!prepare_external_check(&s->check))
5148 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005149 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005150 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5151 return ERR_ALERT | ERR_FATAL;
5152 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005153 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005154
Christopher Faulet61cc8522020-04-20 14:54:42 +02005155 /* A task for a auxiliary agent check */
5156 if (s->agent.state & CHK_ST_CONFIGURED) {
5157 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5158 return ERR_ALERT | ERR_FATAL;
5159 }
5160 srvpos++;
5161 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005162 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005163 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005164 return 0;
5165}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005166
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005167
Christopher Faulet61cc8522020-04-20 14:54:42 +02005168/*
5169 * Return value:
5170 * the port to be used for the health check
5171 * 0 in case no port could be found for the check
5172 */
5173static int srv_check_healthcheck_port(struct check *chk)
5174{
5175 int i = 0;
5176 struct server *srv = NULL;
5177
5178 srv = chk->server;
5179
5180 /* by default, we use the health check port ocnfigured */
5181 if (chk->port > 0)
5182 return chk->port;
5183
5184 /* try to get the port from check_core.addr if check.port not set */
5185 i = get_host_port(&chk->addr);
5186 if (i > 0)
5187 return i;
5188
5189 /* try to get the port from server address */
5190 /* prevent MAPPORTS from working at this point, since checks could
5191 * not be performed in such case (MAPPORTS impose a relative ports
5192 * based on live traffic)
5193 */
5194 if (srv->flags & SRV_F_MAPPORTS)
5195 return 0;
5196
5197 i = srv->svc_port; /* by default */
5198 if (i > 0)
5199 return i;
5200
5201 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005202}
5203
Christopher Faulet61cc8522020-04-20 14:54:42 +02005204/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5205 * if an error occurred.
5206 */
5207static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005208{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005209 const char *err;
5210 struct tcpcheck_rule *r;
5211 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005212
Christopher Faulet61cc8522020-04-20 14:54:42 +02005213 if (!srv->do_check)
5214 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005215
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005216
Christopher Faulet61cc8522020-04-20 14:54:42 +02005217 /* If neither a port nor an addr was specified and no check transport
5218 * layer is forced, then the transport layer used by the checks is the
5219 * same as for the production traffic. Otherwise we use raw_sock by
5220 * default, unless one is specified.
5221 */
5222 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5223 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5224 srv->check.use_ssl = srv->use_ssl;
5225 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005226 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005227 else if (srv->check.use_ssl == 1)
5228 srv->check.xprt = xprt_get(XPRT_SSL);
5229 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005230 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02005231 else if (srv->check.use_ssl == 1)
5232 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005233
Christopher Faulet12882cf2020-04-23 15:50:18 +02005234 /* Inherit the mux protocol from the server if not already defined for
5235 * the check
5236 */
5237 if (srv->mux_proto && !srv->check.mux_proto)
5238 srv->check.mux_proto = srv->mux_proto;
5239
Christopher Faulet61cc8522020-04-20 14:54:42 +02005240 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005241
Christopher Faulet61cc8522020-04-20 14:54:42 +02005242 /* We need at least a service port, a check port or the first tcp-check
5243 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5244 */
5245 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5246 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5247 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005248
Christopher Faulet61cc8522020-04-20 14:54:42 +02005249 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5250 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5251 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5252 ret |= ERR_ALERT | ERR_ABORT;
5253 goto out;
5254 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005255
Christopher Faulet61cc8522020-04-20 14:54:42 +02005256 /* search the first action (connect / send / expect) in the list */
5257 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5258 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5259 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5260 "nor tcp_check rule 'connect' with port information.\n",
5261 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5262 ret |= ERR_ALERT | ERR_ABORT;
5263 goto out;
5264 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005265
Christopher Faulet61cc8522020-04-20 14:54:42 +02005266 /* scan the tcp-check ruleset to ensure a port has been configured */
5267 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5268 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5269 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5270 "and a tcp_check rule 'connect' with no port information.\n",
5271 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5272 ret |= ERR_ALERT | ERR_ABORT;
5273 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005274 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005275 }
5276
Christopher Faulet61cc8522020-04-20 14:54:42 +02005277 init:
5278 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5279 struct tcpcheck_ruleset *rs = NULL;
5280 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5281 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005282
Christopher Faulet61cc8522020-04-20 14:54:42 +02005283 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5284 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005285
Christopher Faulet61cc8522020-04-20 14:54:42 +02005286 rs = find_tcpcheck_ruleset("*tcp-check");
5287 if (!rs) {
5288 rs = create_tcpcheck_ruleset("*tcp-check");
5289 if (rs == NULL) {
5290 ha_alert("config: %s '%s': out of memory.\n",
5291 proxy_type_str(srv->proxy), srv->proxy->id);
5292 ret |= ERR_ALERT | ERR_FATAL;
5293 goto out;
5294 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005295 }
5296
Christopher Faulet61cc8522020-04-20 14:54:42 +02005297 free_tcpcheck_vars(&rules->preset_vars);
5298 rules->list = &rs->rules;
5299 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005300 }
5301
Christopher Faulet61cc8522020-04-20 14:54:42 +02005302 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5303 if (err) {
5304 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5305 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5306 ret |= ERR_ALERT | ERR_ABORT;
5307 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005308 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005309 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5310 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005311
Christopher Faulet61cc8522020-04-20 14:54:42 +02005312 out:
5313 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005314}
5315
Christopher Faulet61cc8522020-04-20 14:54:42 +02005316/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5317 * if an error occurred.
5318 */
5319static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005320{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005321 struct tcpcheck_rule *chk;
5322 const char *err;
5323 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005324
Christopher Faulet61cc8522020-04-20 14:54:42 +02005325 if (!srv->do_agent)
5326 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005327
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005328 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005329 * implicit one is inserted before all others.
5330 */
5331 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5332 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5333 chk = calloc(1, sizeof(*chk));
5334 if (!chk) {
5335 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5336 " to agent-check for server '%s' (out of memory).\n",
5337 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5338 ret |= ERR_ALERT | ERR_FATAL;
5339 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005340 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005341 chk->action = TCPCHK_ACT_CONNECT;
5342 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5343 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005344 }
5345
Christopher Faulete5870d82020-04-15 11:32:03 +02005346
Christopher Faulet61cc8522020-04-20 14:54:42 +02005347 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5348 if (err) {
5349 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5350 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5351 ret |= ERR_ALERT | ERR_ABORT;
5352 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005353 }
5354
Christopher Faulet61cc8522020-04-20 14:54:42 +02005355 if (!srv->agent.inter)
5356 srv->agent.inter = srv->check.inter;
5357
5358 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5359 global.maxsock++;
5360
5361 out:
5362 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005363}
5364
Christopher Faulet61cc8522020-04-20 14:54:42 +02005365/* Check tcp-check health-check configuration for the proxy <px>. */
5366static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005367{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005368 struct tcpcheck_rule *chk, *back;
5369 char *comment = NULL, *errmsg = NULL;
5370 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5371 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005372
Christopher Faulet61cc8522020-04-20 14:54:42 +02005373 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5374 deinit_proxy_tcpcheck(px);
5375 goto out;
5376 }
5377
5378 free(px->check_command);
5379 free(px->check_path);
5380 px->check_command = px->check_path = NULL;
5381
5382 if (!px->tcpcheck_rules.list) {
5383 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5384 ret |= ERR_ALERT | ERR_FATAL;
5385 goto out;
5386 }
5387
5388 /* HTTP ruleset only : */
5389 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5390 struct tcpcheck_rule *next;
5391
5392 /* move remaining implicit send rule from "option httpchk" line to the right place.
5393 * If such rule exists, it must be the first one. In this case, the rule is moved
5394 * after the first connect rule, if any. Otherwise, nothing is done.
5395 */
5396 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5397 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5398 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5399 if (next && next->action == TCPCHK_ACT_CONNECT) {
5400 LIST_DEL(&chk->list);
5401 LIST_ADD(&next->list, &chk->list);
5402 chk->index = next->index;
5403 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005404 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005405
5406 /* add implicit expect rule if the last one is a send. It is inherited from previous
5407 * versions where the http expect rule was optional. Now it is possible to chained
5408 * send/expect rules but the last expect may still be implicit.
5409 */
5410 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5411 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005412 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005413 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5414 px->conf.file, px->conf.line, &errmsg);
5415 if (!next) {
5416 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5417 "(%s).\n", px->id, errmsg);
5418 free(errmsg);
5419 ret |= ERR_ALERT | ERR_FATAL;
5420 goto out;
5421 }
5422 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5423 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005424 }
5425 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005426
5427 /* For all ruleset: */
5428
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005429 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005430 * implicit one is inserted before all others.
5431 */
5432 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5433 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5434 chk = calloc(1, sizeof(*chk));
5435 if (!chk) {
5436 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5437 "(out of memory).\n", px->id);
5438 ret |= ERR_ALERT | ERR_FATAL;
5439 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005440 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005441 chk->action = TCPCHK_ACT_CONNECT;
5442 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5443 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5444 }
5445
5446 /* Remove all comment rules. To do so, when a such rule is found, the
5447 * comment is assigned to the following rule(s).
5448 */
5449 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5450 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5451 free(comment);
5452 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005453 }
5454
Christopher Faulet61cc8522020-04-20 14:54:42 +02005455 prev_action = chk->action;
5456 switch (chk->action) {
5457 case TCPCHK_ACT_COMMENT:
5458 free(comment);
5459 comment = chk->comment;
5460 LIST_DEL(&chk->list);
5461 free(chk);
5462 break;
5463 case TCPCHK_ACT_CONNECT:
5464 if (!chk->comment && comment)
5465 chk->comment = strdup(comment);
5466 /* fall though */
5467 case TCPCHK_ACT_ACTION_KW:
5468 free(comment);
5469 comment = NULL;
5470 break;
5471 case TCPCHK_ACT_SEND:
5472 case TCPCHK_ACT_EXPECT:
5473 if (!chk->comment && comment)
5474 chk->comment = strdup(comment);
5475 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005476 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005477 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005478 free(comment);
5479 comment = NULL;
5480
5481 out:
5482 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005483}
5484
Christopher Faulet61cc8522020-04-20 14:54:42 +02005485void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005486{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005487 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5488 px->tcpcheck_rules.flags = 0;
5489 px->tcpcheck_rules.list = NULL;
5490}
Christopher Faulete5870d82020-04-15 11:32:03 +02005491
Christopher Faulet61cc8522020-04-20 14:54:42 +02005492static void deinit_srv_check(struct server *srv)
5493{
5494 if (srv->check.state & CHK_ST_CONFIGURED)
5495 free_check(&srv->check);
5496 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5497 srv->do_check = 0;
5498}
Christopher Faulete5870d82020-04-15 11:32:03 +02005499
Christopher Faulet61cc8522020-04-20 14:54:42 +02005500
5501static void deinit_srv_agent_check(struct server *srv)
5502{
5503 if (srv->agent.tcpcheck_rules) {
5504 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5505 free(srv->agent.tcpcheck_rules);
5506 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005507 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005508
Christopher Faulet61cc8522020-04-20 14:54:42 +02005509 if (srv->agent.state & CHK_ST_CONFIGURED)
5510 free_check(&srv->agent);
5511
5512 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5513 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005514}
5515
Christopher Faulet61cc8522020-04-20 14:54:42 +02005516static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005517{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005518 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005519 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005520 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005521
Christopher Fauletd7cee712020-04-21 13:45:00 +02005522 node = ebpt_first(&shared_tcpchecks);
5523 while (node) {
5524 next = ebpt_next(node);
5525 ebpt_delete(node);
5526 free(node->key);
5527 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005528 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5529 LIST_DEL(&r->list);
5530 free_tcpcheck(r, 0);
5531 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005532 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005533 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005534 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005535}
Christopher Faulete5870d82020-04-15 11:32:03 +02005536
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005537
Christopher Faulet61cc8522020-04-20 14:54:42 +02005538REGISTER_POST_SERVER_CHECK(init_srv_check);
5539REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5540REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5541REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005542
Christopher Faulet61cc8522020-04-20 14:54:42 +02005543REGISTER_SERVER_DEINIT(deinit_srv_check);
5544REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5545REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5546REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005547
Christopher Faulet61cc8522020-04-20 14:54:42 +02005548/**************************************************************************/
5549/****************************** Email alerts ******************************/
5550/* NOTE: It may be pertinent to use an applet to handle email alerts */
5551/* instead of a tcp-check ruleset */
5552/**************************************************************************/
5553void email_alert_free(struct email_alert *alert)
5554{
5555 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005556
Christopher Faulet61cc8522020-04-20 14:54:42 +02005557 if (!alert)
5558 return;
5559
5560 if (alert->rules.list) {
5561 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5562 LIST_DEL(&rule->list);
5563 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005564 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005565 free_tcpcheck_vars(&alert->rules.preset_vars);
5566 free(alert->rules.list);
5567 alert->rules.list = NULL;
5568 }
5569 pool_free(pool_head_email_alert, alert);
5570}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005571
Christopher Faulet61cc8522020-04-20 14:54:42 +02005572static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5573{
5574 struct check *check = context;
5575 struct email_alertq *q;
5576 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005577
Christopher Faulet61cc8522020-04-20 14:54:42 +02005578 q = container_of(check, typeof(*q), check);
5579
5580 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5581 while (1) {
5582 if (!(check->state & CHK_ST_ENABLED)) {
5583 if (LIST_ISEMPTY(&q->email_alerts)) {
5584 /* All alerts processed, queue the task */
5585 t->expire = TICK_ETERNITY;
5586 task_queue(t);
5587 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005588 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005589
5590 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5591 LIST_DEL(&alert->list);
5592 t->expire = now_ms;
5593 check->tcpcheck_rules = &alert->rules;
5594 check->status = HCHK_STATUS_INI;
5595 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005596 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005597
5598 process_chk(t, context, state);
5599 if (check->state & CHK_ST_INPROGRESS)
5600 break;
5601
5602 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5603 email_alert_free(alert);
5604 check->tcpcheck_rules = NULL;
5605 check->server = NULL;
5606 check->state &= ~CHK_ST_ENABLED;
5607 }
5608 end:
5609 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5610 return t;
5611}
5612
5613/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5614 *
5615 * The function returns 1 in success case, otherwise, it returns 0 and err is
5616 * filled.
5617 */
5618int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5619{
5620 struct mailer *mailer;
5621 struct email_alertq *queues;
5622 const char *err_str;
5623 int i = 0;
5624
5625 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5626 memprintf(err, "out of memory while allocating mailer alerts queues");
5627 goto fail_no_queue;
5628 }
5629
5630 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5631 struct email_alertq *q = &queues[i];
5632 struct check *check = &q->check;
5633 struct task *t;
5634
5635 LIST_INIT(&q->email_alerts);
5636 HA_SPIN_INIT(&q->lock);
5637 check->inter = mls->timeout.mail;
5638 check->rise = DEF_AGENT_RISETIME;
5639 check->proxy = p;
5640 check->fall = DEF_AGENT_FALLTIME;
5641 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5642 memprintf(err, "%s", err_str);
5643 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005644 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005645
5646 check->xprt = mailer->xprt;
5647 check->addr = mailer->addr;
5648 check->port = get_host_port(&mailer->addr);
5649
5650 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5651 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005652 goto error;
5653 }
5654
Christopher Faulet61cc8522020-04-20 14:54:42 +02005655 check->task = t;
5656 t->process = process_email_alert;
5657 t->context = check;
5658
5659 /* check this in one ms */
5660 t->expire = TICK_ETERNITY;
5661 check->start = now;
5662 task_queue(t);
5663 }
5664
5665 mls->users++;
5666 free(p->email_alert.mailers.name);
5667 p->email_alert.mailers.m = mls;
5668 p->email_alert.queues = queues;
5669 return 0;
5670
5671 error:
5672 for (i = 0; i < mls->count; i++) {
5673 struct email_alertq *q = &queues[i];
5674 struct check *check = &q->check;
5675
5676 free_check(check);
5677 }
5678 free(queues);
5679 fail_no_queue:
5680 return 1;
5681}
5682
5683static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5684{
5685 struct tcpcheck_rule *tcpcheck, *prev_check;
5686 struct tcpcheck_expect *expect;
5687
5688 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5689 return 0;
5690 memset(tcpcheck, 0, sizeof(*tcpcheck));
5691 tcpcheck->action = TCPCHK_ACT_EXPECT;
5692
5693 expect = &tcpcheck->expect;
5694 expect->type = TCPCHK_EXPECT_STRING;
5695 LIST_INIT(&expect->onerror_fmt);
5696 LIST_INIT(&expect->onsuccess_fmt);
5697 expect->ok_status = HCHK_STATUS_L7OKD;
5698 expect->err_status = HCHK_STATUS_L7RSP;
5699 expect->tout_status = HCHK_STATUS_L7TOUT;
5700 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005701 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005702 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5703 return 0;
5704 }
5705
5706 /* All tcp-check expect points back to the first inverse expect rule
5707 * in a chain of one or more expect rule, potentially itself.
5708 */
5709 tcpcheck->expect.head = tcpcheck;
5710 list_for_each_entry_rev(prev_check, rules->list, list) {
5711 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5712 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5713 tcpcheck->expect.head = prev_check;
5714 continue;
5715 }
5716 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5717 break;
5718 }
5719 LIST_ADDQ(rules->list, &tcpcheck->list);
5720 return 1;
5721}
5722
5723static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5724{
5725 struct tcpcheck_rule *tcpcheck;
5726 struct tcpcheck_send *send;
5727 const char *in;
5728 char *dst;
5729 int i;
5730
5731 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5732 return 0;
5733 memset(tcpcheck, 0, sizeof(*tcpcheck));
5734 tcpcheck->action = TCPCHK_ACT_SEND;
5735
5736 send = &tcpcheck->send;
5737 send->type = TCPCHK_SEND_STRING;
5738
5739 for (i = 0; strs[i]; i++)
5740 send->data.len += strlen(strs[i]);
5741
Christopher Fauletb61caf42020-04-21 10:57:42 +02005742 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005743 if (!isttest(send->data)) {
5744 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5745 return 0;
5746 }
5747
Christopher Fauletb61caf42020-04-21 10:57:42 +02005748 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005749 for (i = 0; strs[i]; i++)
5750 for (in = strs[i]; (*dst = *in++); dst++);
5751 *dst = 0;
5752
5753 LIST_ADDQ(rules->list, &tcpcheck->list);
5754 return 1;
5755}
5756
5757static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5758 struct email_alertq *q, const char *msg)
5759{
5760 struct email_alert *alert;
5761 struct tcpcheck_rule *tcpcheck;
5762 struct check *check = &q->check;
5763
5764 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5765 goto error;
5766 LIST_INIT(&alert->list);
5767 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5768 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5769 if (!alert->rules.list)
5770 goto error;
5771 LIST_INIT(alert->rules.list);
5772 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5773 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005774
Christopher Faulet61cc8522020-04-20 14:54:42 +02005775 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5776 goto error;
5777 memset(tcpcheck, 0, sizeof(*tcpcheck));
5778 tcpcheck->action = TCPCHK_ACT_CONNECT;
5779 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005780
Christopher Faulet61cc8522020-04-20 14:54:42 +02005781 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005782
Christopher Faulet61cc8522020-04-20 14:54:42 +02005783 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005784 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005785
5786 {
5787 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5788 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5789 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005790 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005791
Christopher Faulet61cc8522020-04-20 14:54:42 +02005792 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5793 goto error;
5794
5795 {
5796 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5797 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005798 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005799 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005800
5801 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5802 goto error;
5803
5804 {
5805 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5806 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005807 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005808 }
5809
Christopher Faulet61cc8522020-04-20 14:54:42 +02005810 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5811 goto error;
5812
5813 {
5814 const char * const strs[2] = { "DATA\r\n" };
5815 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005816 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005817 }
5818
5819 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5820 goto error;
5821
5822 {
5823 struct tm tm;
5824 char datestr[48];
5825 const char * const strs[18] = {
5826 "From: ", p->email_alert.from, "\r\n",
5827 "To: ", p->email_alert.to, "\r\n",
5828 "Date: ", datestr, "\r\n",
5829 "Subject: [HAproxy Alert] ", msg, "\r\n",
5830 "\r\n",
5831 msg, "\r\n",
5832 "\r\n",
5833 ".\r\n",
5834 NULL
5835 };
5836
5837 get_localtime(date.tv_sec, &tm);
5838
5839 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005840 goto error;
5841 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005842
5843 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005844 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005845 }
5846
5847 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005848 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005849
5850 {
5851 const char * const strs[2] = { "QUIT\r\n" };
5852 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5853 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005854 }
5855
Christopher Faulet61cc8522020-04-20 14:54:42 +02005856 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5857 goto error;
5858
5859 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5860 task_wakeup(check->task, TASK_WOKEN_MSG);
5861 LIST_ADDQ(&q->email_alerts, &alert->list);
5862 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5863 return 1;
5864
5865error:
5866 email_alert_free(alert);
5867 return 0;
5868}
5869
5870static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5871{
5872 int i;
5873 struct mailer *mailer;
5874
5875 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5876 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5877 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5878 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5879 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005880 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005881 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005882
Christopher Faulet61cc8522020-04-20 14:54:42 +02005883 return;
5884}
5885
5886/*
5887 * Send email alert if configured.
5888 */
5889void send_email_alert(struct server *s, int level, const char *format, ...)
5890{
5891 va_list argp;
5892 char buf[1024];
5893 int len;
5894 struct proxy *p = s->proxy;
5895
5896 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5897 return;
5898
5899 va_start(argp, format);
5900 len = vsnprintf(buf, sizeof(buf), format, argp);
5901 va_end(argp);
5902
5903 if (len < 0 || len >= sizeof(buf)) {
5904 ha_alert("Email alert [%s] could not format message\n", p->id);
5905 return;
5906 }
5907
5908 enqueue_email_alert(p, s, buf);
5909}
5910
5911/**************************************************************************/
5912/************************** Check sample fetches **************************/
5913/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005914
Christopher Faulet61cc8522020-04-20 14:54:42 +02005915static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005916 { /* END */ },
5917}};
5918
5919INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5920
5921
5922/**************************************************************************/
5923/************************ Check's parsing functions ***********************/
5924/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005925/* Parses the "tcp-check" proxy keyword */
5926static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5927 struct proxy *defpx, const char *file, int line,
5928 char **errmsg)
5929{
Christopher Faulet404f9192020-04-09 23:13:54 +02005930 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005931 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005932 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005933
5934 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5935 ret = 1;
5936
Christopher Faulet404f9192020-04-09 23:13:54 +02005937 /* Deduce the ruleset name from the proxy info */
5938 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5939 ((curpx == defpx) ? "defaults" : curpx->id),
5940 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005941
Christopher Faulet61cc8522020-04-20 14:54:42 +02005942 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005943 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005944 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005945 if (rs == NULL) {
5946 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005947 goto error;
5948 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005949 }
5950
Gaetan Rivet5301b012020-02-25 17:19:17 +01005951 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005952 if (!LIST_ISEMPTY(&rs->rules)) {
5953 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005954 index = chk->index + 1;
5955 }
5956
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005957 cur_arg = 1;
5958 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005959 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005960 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5961 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005962 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005963 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005964 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005965 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005966 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005967 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005968 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5969
5970 if (!kw) {
5971 action_kw_tcp_check_build_list(&trash);
5972 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5973 "%s%s. but got '%s'",
5974 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5975 goto error;
5976 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005977 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005978 }
5979
5980 if (!chk) {
5981 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5982 goto error;
5983 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005984 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005985
5986 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005987 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005988 LIST_ADDQ(&rs->rules, &chk->list);
5989
5990 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005991 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005992 /* Use this ruleset if the proxy already has tcp-check enabled */
5993 curpx->tcpcheck_rules.list = &rs->rules;
5994 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5995 }
5996 else {
5997 /* mark this ruleset as unused for now */
5998 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
5999 }
6000
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006001 return ret;
6002
6003 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006004 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006005 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006006 return -1;
6007}
6008
Christopher Faulet51b129f2020-04-09 15:54:18 +02006009/* Parses the "http-check" proxy keyword */
6010static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6011 struct proxy *defpx, const char *file, int line,
6012 char **errmsg)
6013{
Christopher Faulete5870d82020-04-15 11:32:03 +02006014 struct tcpcheck_ruleset *rs = NULL;
6015 struct tcpcheck_rule *chk = NULL;
6016 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006017
6018 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6019 ret = 1;
6020
6021 cur_arg = 1;
6022 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6023 /* enable a graceful server shutdown on an HTTP 404 response */
6024 curpx->options |= PR_O_DISABLE404;
6025 if (too_many_args(1, args, errmsg, NULL))
6026 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006027 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006028 }
6029 else if (strcmp(args[cur_arg], "send-state") == 0) {
6030 /* enable emission of the apparent state of a server in HTTP checks */
6031 curpx->options2 |= PR_O2_CHK_SNDST;
6032 if (too_many_args(1, args, errmsg, NULL))
6033 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006034 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006035 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006036
Christopher Faulete5870d82020-04-15 11:32:03 +02006037 /* Deduce the ruleset name from the proxy info */
6038 chunk_printf(&trash, "*http-check-%s_%s-%d",
6039 ((curpx == defpx) ? "defaults" : curpx->id),
6040 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006041
Christopher Faulet61cc8522020-04-20 14:54:42 +02006042 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006043 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006044 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006045 if (rs == NULL) {
6046 memprintf(errmsg, "out of memory.\n");
6047 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006048 }
6049 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006050
Christopher Faulete5870d82020-04-15 11:32:03 +02006051 index = 0;
6052 if (!LIST_ISEMPTY(&rs->rules)) {
6053 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6054 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6055 index = chk->index + 1;
6056 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006057
Christopher Faulete5870d82020-04-15 11:32:03 +02006058 if (strcmp(args[cur_arg], "connect") == 0)
6059 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6060 else if (strcmp(args[cur_arg], "send") == 0)
6061 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6062 else if (strcmp(args[cur_arg], "expect") == 0)
6063 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6064 file, line, errmsg);
6065 else if (strcmp(args[cur_arg], "comment") == 0)
6066 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6067 else {
6068 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006069
Christopher Faulete5870d82020-04-15 11:32:03 +02006070 if (!kw) {
6071 action_kw_tcp_check_build_list(&trash);
6072 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6073 " 'send', 'expect'%s%s. but got '%s'",
6074 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6075 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006076 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006077 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6078 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006079
Christopher Faulete5870d82020-04-15 11:32:03 +02006080 if (!chk) {
6081 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6082 goto error;
6083 }
6084 ret = (*errmsg != NULL); /* Handle warning */
6085
6086 chk->index = index;
6087 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6088 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6089 /* Use this ruleset if the proxy already has http-check enabled */
6090 curpx->tcpcheck_rules.list = &rs->rules;
6091 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6092 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6093 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6094 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006095 goto error;
6096 }
6097 }
6098 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006099 /* mark this ruleset as unused for now */
6100 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6101 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006102 }
6103
Christopher Faulete5870d82020-04-15 11:32:03 +02006104 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006105 return ret;
6106
6107 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006108 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006109 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006110 return -1;
6111}
6112
Christopher Faulete9111b62020-04-09 18:12:08 +02006113/* Parses the "external-check" proxy keyword */
6114static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6115 struct proxy *defpx, const char *file, int line,
6116 char **errmsg)
6117{
6118 int cur_arg, ret = 0;
6119
6120 cur_arg = 1;
6121 if (!*(args[cur_arg])) {
6122 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6123 goto error;
6124 }
6125
6126 if (strcmp(args[cur_arg], "command") == 0) {
6127 if (too_many_args(2, args, errmsg, NULL))
6128 goto error;
6129 if (!*(args[cur_arg+1])) {
6130 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6131 goto error;
6132 }
6133 free(curpx->check_command);
6134 curpx->check_command = strdup(args[cur_arg+1]);
6135 }
6136 else if (strcmp(args[cur_arg], "path") == 0) {
6137 if (too_many_args(2, args, errmsg, NULL))
6138 goto error;
6139 if (!*(args[cur_arg+1])) {
6140 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6141 goto error;
6142 }
6143 free(curpx->check_path);
6144 curpx->check_path = strdup(args[cur_arg+1]);
6145 }
6146 else {
6147 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6148 args[0], args[1]);
6149 goto error;
6150 }
6151
6152 ret = (*errmsg != NULL); /* Handle warning */
6153 return ret;
6154
6155error:
6156 return -1;
6157}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006158
Christopher Faulet430e4802020-04-09 15:28:16 +02006159/* Parses the "option tcp-check" proxy keyword */
6160int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6161 const char *file, int line)
6162{
Christopher Faulet404f9192020-04-09 23:13:54 +02006163 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006164 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6165 int err_code = 0;
6166
6167 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6168 err_code |= ERR_WARN;
6169
6170 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6171 goto out;
6172
Christopher Faulet404f9192020-04-09 23:13:54 +02006173 curpx->options2 &= ~PR_O2_CHK_ANY;
6174 curpx->options2 |= PR_O2_TCPCHK_CHK;
6175
Christopher Fauletd7e63962020-04-17 20:15:59 +02006176 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006177 /* If a tcp-check rulesset is already set, do nothing */
6178 if (rules->list)
6179 goto out;
6180
6181 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6182 * get it.
6183 */
6184 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6185 goto curpx_ruleset;
6186
6187 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6188 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006189 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006190 if (rs)
6191 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006192 }
6193
Christopher Faulet404f9192020-04-09 23:13:54 +02006194 curpx_ruleset:
6195 /* Deduce the ruleset name from the proxy info */
6196 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6197 ((curpx == defpx) ? "defaults" : curpx->id),
6198 curpx->conf.file, curpx->conf.line);
6199
Christopher Faulet61cc8522020-04-20 14:54:42 +02006200 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006201 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006202 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006203 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006204 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6205 goto error;
6206 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006207 }
6208
Christopher Faulet404f9192020-04-09 23:13:54 +02006209 ruleset_found:
6210 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006211 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006212 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006213
6214 out:
6215 return err_code;
6216
6217 error:
6218 err_code |= ERR_ALERT | ERR_FATAL;
6219 goto out;
6220}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006221
6222/* Parses the "option redis-check" proxy keyword */
6223int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6224 const char *file, int line)
6225{
6226 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6227 static char *redis_res = "+PONG\r\n";
6228
6229 struct tcpcheck_ruleset *rs = NULL;
6230 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6231 struct tcpcheck_rule *chk;
6232 char *errmsg = NULL;
6233 int err_code = 0;
6234
6235 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6236 err_code |= ERR_WARN;
6237
6238 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6239 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006240
6241 curpx->options2 &= ~PR_O2_CHK_ANY;
6242 curpx->options2 |= PR_O2_TCPCHK_CHK;
6243
6244 free_tcpcheck_vars(&rules->preset_vars);
6245 rules->list = NULL;
6246 rules->flags = 0;
6247
Christopher Faulet61cc8522020-04-20 14:54:42 +02006248 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006249 if (rs)
6250 goto ruleset_found;
6251
Christopher Faulet61cc8522020-04-20 14:54:42 +02006252 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006253 if (rs == NULL) {
6254 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6255 goto error;
6256 }
6257
6258 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6259 1, curpx, &rs->rules, file, line, &errmsg);
6260 if (!chk) {
6261 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6262 goto error;
6263 }
6264 chk->index = 0;
6265 LIST_ADDQ(&rs->rules, &chk->list);
6266
6267 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6268 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006269 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006270 "on-success", "Redis server is ok",
6271 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006272 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006273 if (!chk) {
6274 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6275 goto error;
6276 }
6277 chk->index = 1;
6278 LIST_ADDQ(&rs->rules, &chk->list);
6279
Christopher Faulet33f05df2020-04-01 11:08:50 +02006280 ruleset_found:
6281 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006282 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006283
6284 out:
6285 free(errmsg);
6286 return err_code;
6287
6288 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006289 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006290 err_code |= ERR_ALERT | ERR_FATAL;
6291 goto out;
6292}
6293
Christopher Faulet811f78c2020-04-01 11:10:27 +02006294
6295/* Parses the "option ssl-hello-chk" proxy keyword */
6296int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6297 const char *file, int line)
6298{
6299 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6300 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6301 *
6302 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6303 */
6304 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006305 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006306 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6307 "0079" /* ContentLength : 0x79 bytes after this one */
6308 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6309 "000075" /* HandshakeLength : 0x75 bytes after this one */
6310 "0300" /* Hello Version : 0x0300 = v3 */
6311 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6312 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6313 "00" /* Session ID length : empty (no session ID) */
6314 "004E" /* Cipher Suite Length : 78 bytes after this one */
6315 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6316 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6317 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6318 "000D" "000E" "000F" "0010" /* various bit lengths, */
6319 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6320 "0015" "0016" "0017" "0018"
6321 "0019" "001A" "001B" "002F"
6322 "0030" "0031" "0032" "0033"
6323 "0034" "0035" "0036" "0037"
6324 "0038" "0039" "003A"
6325 "01" /* Compression Length : 0x01 = 1 byte for types */
6326 "00" /* Compression Type : 0x00 = NULL compression */
6327 };
6328
6329 struct tcpcheck_ruleset *rs = NULL;
6330 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6331 struct tcpcheck_rule *chk;
6332 char *errmsg = NULL;
6333 int err_code = 0;
6334
6335 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6336 err_code |= ERR_WARN;
6337
6338 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6339 goto out;
6340
Christopher Faulet811f78c2020-04-01 11:10:27 +02006341 curpx->options2 &= ~PR_O2_CHK_ANY;
6342 curpx->options2 |= PR_O2_TCPCHK_CHK;
6343
6344 free_tcpcheck_vars(&rules->preset_vars);
6345 rules->list = NULL;
6346 rules->flags = 0;
6347
Christopher Faulet61cc8522020-04-20 14:54:42 +02006348 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006349 if (rs)
6350 goto ruleset_found;
6351
Christopher Faulet61cc8522020-04-20 14:54:42 +02006352 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006353 if (rs == NULL) {
6354 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6355 goto error;
6356 }
6357
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006358 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006359 1, curpx, &rs->rules, file, line, &errmsg);
6360 if (!chk) {
6361 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6362 goto error;
6363 }
6364 chk->index = 0;
6365 LIST_ADDQ(&rs->rules, &chk->list);
6366
6367 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006368 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006369 "error-status", "L6RSP", "tout-status", "L6TOUT",
6370 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006371 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006372 if (!chk) {
6373 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6374 goto error;
6375 }
6376 chk->index = 1;
6377 LIST_ADDQ(&rs->rules, &chk->list);
6378
Christopher Faulet811f78c2020-04-01 11:10:27 +02006379 ruleset_found:
6380 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006381 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006382
6383 out:
6384 free(errmsg);
6385 return err_code;
6386
6387 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006388 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006389 err_code |= ERR_ALERT | ERR_FATAL;
6390 goto out;
6391}
6392
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006393/* Parses the "option smtpchk" proxy keyword */
6394int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6395 const char *file, int line)
6396{
6397 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6398
6399 struct tcpcheck_ruleset *rs = NULL;
6400 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6401 struct tcpcheck_rule *chk;
6402 struct tcpcheck_var *var = NULL;
6403 char *cmd = NULL, *errmsg = NULL;
6404 int err_code = 0;
6405
6406 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6407 err_code |= ERR_WARN;
6408
6409 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6410 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006411
6412 curpx->options2 &= ~PR_O2_CHK_ANY;
6413 curpx->options2 |= PR_O2_TCPCHK_CHK;
6414
6415 free_tcpcheck_vars(&rules->preset_vars);
6416 rules->list = NULL;
6417 rules->flags = 0;
6418
6419 cur_arg += 2;
6420 if (*args[cur_arg] && *args[cur_arg+1] &&
6421 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6422 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6423 if (cmd)
6424 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6425 }
6426 else {
6427 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6428 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6429 cmd = strdup("HELO localhost");
6430 }
6431
Christopher Fauletb61caf42020-04-21 10:57:42 +02006432 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006433 if (cmd == NULL || var == NULL) {
6434 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6435 goto error;
6436 }
6437 var->data.type = SMP_T_STR;
6438 var->data.u.str.area = cmd;
6439 var->data.u.str.data = strlen(cmd);
6440 LIST_INIT(&var->list);
6441 LIST_ADDQ(&rules->preset_vars, &var->list);
6442 cmd = NULL;
6443 var = NULL;
6444
Christopher Faulet61cc8522020-04-20 14:54:42 +02006445 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006446 if (rs)
6447 goto ruleset_found;
6448
Christopher Faulet61cc8522020-04-20 14:54:42 +02006449 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006450 if (rs == NULL) {
6451 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6452 goto error;
6453 }
6454
6455 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6456 1, curpx, &rs->rules, file, line, &errmsg);
6457 if (!chk) {
6458 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6459 goto error;
6460 }
6461 chk->index = 0;
6462 LIST_ADDQ(&rs->rules, &chk->list);
6463
6464 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6465 "min-recv", "4",
6466 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006467 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006468 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006469 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006470 if (!chk) {
6471 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6472 goto error;
6473 }
6474 chk->index = 1;
6475 LIST_ADDQ(&rs->rules, &chk->list);
6476
6477 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6478 "min-recv", "4",
6479 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006480 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6481 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006482 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006483 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006484 if (!chk) {
6485 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6486 goto error;
6487 }
6488 chk->index = 2;
6489 LIST_ADDQ(&rs->rules, &chk->list);
6490
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006491 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006492 1, curpx, &rs->rules, file, line, &errmsg);
6493 if (!chk) {
6494 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6495 goto error;
6496 }
6497 chk->index = 3;
6498 LIST_ADDQ(&rs->rules, &chk->list);
6499
6500 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6501 "min-recv", "4",
6502 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006503 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6504 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6505 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006506 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006507 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006508 if (!chk) {
6509 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6510 goto error;
6511 }
6512 chk->index = 4;
6513 LIST_ADDQ(&rs->rules, &chk->list);
6514
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006515 ruleset_found:
6516 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006517 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006518
6519 out:
6520 free(errmsg);
6521 return err_code;
6522
6523 error:
6524 free(cmd);
6525 free(var);
6526 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006527 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006528 err_code |= ERR_ALERT | ERR_FATAL;
6529 goto out;
6530}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006531
Christopher Fauletce355072020-04-02 11:44:39 +02006532/* Parses the "option pgsql-check" proxy keyword */
6533int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6534 const char *file, int line)
6535{
6536 static char pgsql_req[] = {
6537 "%[var(check.plen),htonl,hex]" /* The packet length*/
6538 "00030000" /* the version 3.0 */
6539 "7573657200" /* "user" key */
6540 "%[var(check.username),hex]00" /* the username */
6541 "00"
6542 };
6543
6544 struct tcpcheck_ruleset *rs = NULL;
6545 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6546 struct tcpcheck_rule *chk;
6547 struct tcpcheck_var *var = NULL;
6548 char *user = NULL, *errmsg = NULL;
6549 size_t packetlen = 0;
6550 int err_code = 0;
6551
6552 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6553 err_code |= ERR_WARN;
6554
6555 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6556 goto out;
6557
Christopher Fauletce355072020-04-02 11:44:39 +02006558 curpx->options2 &= ~PR_O2_CHK_ANY;
6559 curpx->options2 |= PR_O2_TCPCHK_CHK;
6560
6561 free_tcpcheck_vars(&rules->preset_vars);
6562 rules->list = NULL;
6563 rules->flags = 0;
6564
6565 cur_arg += 2;
6566 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6567 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6568 file, line, args[0], args[1]);
6569 goto error;
6570 }
6571 if (strcmp(args[cur_arg], "user") == 0) {
6572 packetlen = 15 + strlen(args[cur_arg+1]);
6573 user = strdup(args[cur_arg+1]);
6574
Christopher Fauletb61caf42020-04-21 10:57:42 +02006575 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006576 if (user == NULL || var == NULL) {
6577 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6578 goto error;
6579 }
6580 var->data.type = SMP_T_STR;
6581 var->data.u.str.area = user;
6582 var->data.u.str.data = strlen(user);
6583 LIST_INIT(&var->list);
6584 LIST_ADDQ(&rules->preset_vars, &var->list);
6585 user = NULL;
6586 var = NULL;
6587
Christopher Fauletb61caf42020-04-21 10:57:42 +02006588 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006589 if (var == NULL) {
6590 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6591 goto error;
6592 }
6593 var->data.type = SMP_T_SINT;
6594 var->data.u.sint = packetlen;
6595 LIST_INIT(&var->list);
6596 LIST_ADDQ(&rules->preset_vars, &var->list);
6597 var = NULL;
6598 }
6599 else {
6600 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6601 file, line, args[0], args[1]);
6602 goto error;
6603 }
6604
Christopher Faulet61cc8522020-04-20 14:54:42 +02006605 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006606 if (rs)
6607 goto ruleset_found;
6608
Christopher Faulet61cc8522020-04-20 14:54:42 +02006609 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006610 if (rs == NULL) {
6611 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6612 goto error;
6613 }
6614
6615 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6616 1, curpx, &rs->rules, file, line, &errmsg);
6617 if (!chk) {
6618 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6619 goto error;
6620 }
6621 chk->index = 0;
6622 LIST_ADDQ(&rs->rules, &chk->list);
6623
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006624 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006625 1, curpx, &rs->rules, file, line, &errmsg);
6626 if (!chk) {
6627 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6628 goto error;
6629 }
6630 chk->index = 1;
6631 LIST_ADDQ(&rs->rules, &chk->list);
6632
6633 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6634 "min-recv", "5",
6635 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006636 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006637 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006638 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006639 if (!chk) {
6640 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6641 goto error;
6642 }
6643 chk->index = 2;
6644 LIST_ADDQ(&rs->rules, &chk->list);
6645
Christopher Fauletb841c742020-04-27 18:29:49 +02006646 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 +02006647 "min-recv", "9",
6648 "error-status", "L7STS",
6649 "on-success", "PostgreSQL server is ok",
6650 "on-error", "PostgreSQL unknown error",
6651 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006652 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006653 if (!chk) {
6654 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6655 goto error;
6656 }
6657 chk->index = 3;
6658 LIST_ADDQ(&rs->rules, &chk->list);
6659
Christopher Fauletce355072020-04-02 11:44:39 +02006660 ruleset_found:
6661 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006662 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006663
6664 out:
6665 free(errmsg);
6666 return err_code;
6667
6668 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006669 free(user);
6670 free(var);
6671 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006672 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006673 err_code |= ERR_ALERT | ERR_FATAL;
6674 goto out;
6675}
6676
6677
6678/* Parses the "option mysql-check" proxy keyword */
6679int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6680 const char *file, int line)
6681{
6682 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6683 * const char mysql40_client_auth_pkt[] = {
6684 * "\x0e\x00\x00" // packet length
6685 * "\x01" // packet number
6686 * "\x00\x00" // client capabilities
6687 * "\x00\x00\x01" // max packet
6688 * "haproxy\x00" // username (null terminated string)
6689 * "\x00" // filler (always 0x00)
6690 * "\x01\x00\x00" // packet length
6691 * "\x00" // packet number
6692 * "\x01" // COM_QUIT command
6693 * };
6694 */
6695 static char mysql40_rsname[] = "*mysql40-check";
6696 static char mysql40_req[] = {
6697 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6698 "0080" /* client capabilities */
6699 "000001" /* max packet */
6700 "%[var(check.username),hex]00" /* the username */
6701 "00" /* filler (always 0x00) */
6702 "010000" /* packet length*/
6703 "00" /* sequence ID */
6704 "01" /* COM_QUIT command */
6705 };
6706
6707 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6708 * const char mysql41_client_auth_pkt[] = {
6709 * "\x0e\x00\x00\" // packet length
6710 * "\x01" // packet number
6711 * "\x00\x00\x00\x00" // client capabilities
6712 * "\x00\x00\x00\x01" // max packet
6713 * "\x21" // character set (UTF-8)
6714 * char[23] // All zeroes
6715 * "haproxy\x00" // username (null terminated string)
6716 * "\x00" // filler (always 0x00)
6717 * "\x01\x00\x00" // packet length
6718 * "\x00" // packet number
6719 * "\x01" // COM_QUIT command
6720 * };
6721 */
6722 static char mysql41_rsname[] = "*mysql41-check";
6723 static char mysql41_req[] = {
6724 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6725 "00820000" /* client capabilities */
6726 "00800001" /* max packet */
6727 "21" /* character set (UTF-8) */
6728 "000000000000000000000000" /* 23 bytes, al zeroes */
6729 "0000000000000000000000"
6730 "%[var(check.username),hex]00" /* the username */
6731 "00" /* filler (always 0x00) */
6732 "010000" /* packet length*/
6733 "00" /* sequence ID */
6734 "01" /* COM_QUIT command */
6735 };
6736
6737 struct tcpcheck_ruleset *rs = NULL;
6738 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6739 struct tcpcheck_rule *chk;
6740 struct tcpcheck_var *var = NULL;
6741 char *mysql_rsname = "*mysql-check";
6742 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6743 int index = 0, err_code = 0;
6744
6745 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6746 err_code |= ERR_WARN;
6747
6748 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6749 goto out;
6750
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006751 curpx->options2 &= ~PR_O2_CHK_ANY;
6752 curpx->options2 |= PR_O2_TCPCHK_CHK;
6753
6754 free_tcpcheck_vars(&rules->preset_vars);
6755 rules->list = NULL;
6756 rules->flags = 0;
6757
6758 cur_arg += 2;
6759 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006760 int packetlen, userlen;
6761
6762 if (strcmp(args[cur_arg], "user") != 0) {
6763 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6764 file, line, args[0], args[1], args[cur_arg]);
6765 goto error;
6766 }
6767
6768 if (*(args[cur_arg+1]) == 0) {
6769 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6770 file, line, args[0], args[1], args[cur_arg]);
6771 goto error;
6772 }
6773
6774 hdr = calloc(4, sizeof(*hdr));
6775 user = strdup(args[cur_arg+1]);
6776 userlen = strlen(args[cur_arg+1]);
6777
6778 if (hdr == NULL || user == NULL) {
6779 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6780 goto error;
6781 }
6782
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006783 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006784 packetlen = userlen + 7 + 27;
6785 mysql_req = mysql41_req;
6786 mysql_rsname = mysql41_rsname;
6787 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006788 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006789 packetlen = userlen + 7;
6790 mysql_req = mysql40_req;
6791 mysql_rsname = mysql40_rsname;
6792 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006793 else {
6794 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
6795 file, line, args[cur_arg], args[cur_arg+2]);
6796 goto error;
6797 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006798
6799 hdr[0] = (unsigned char)(packetlen & 0xff);
6800 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6801 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6802 hdr[3] = 1;
6803
Christopher Fauletb61caf42020-04-21 10:57:42 +02006804 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006805 if (var == NULL) {
6806 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6807 goto error;
6808 }
6809 var->data.type = SMP_T_STR;
6810 var->data.u.str.area = hdr;
6811 var->data.u.str.data = 4;
6812 LIST_INIT(&var->list);
6813 LIST_ADDQ(&rules->preset_vars, &var->list);
6814 hdr = NULL;
6815 var = NULL;
6816
Christopher Fauletb61caf42020-04-21 10:57:42 +02006817 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006818 if (var == NULL) {
6819 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6820 goto error;
6821 }
6822 var->data.type = SMP_T_STR;
6823 var->data.u.str.area = user;
6824 var->data.u.str.data = strlen(user);
6825 LIST_INIT(&var->list);
6826 LIST_ADDQ(&rules->preset_vars, &var->list);
6827 user = NULL;
6828 var = NULL;
6829 }
6830
Christopher Faulet61cc8522020-04-20 14:54:42 +02006831 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006832 if (rs)
6833 goto ruleset_found;
6834
Christopher Faulet61cc8522020-04-20 14:54:42 +02006835 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006836 if (rs == NULL) {
6837 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6838 goto error;
6839 }
6840
6841 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6842 1, curpx, &rs->rules, file, line, &errmsg);
6843 if (!chk) {
6844 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6845 goto error;
6846 }
6847 chk->index = index++;
6848 LIST_ADDQ(&rs->rules, &chk->list);
6849
6850 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006851 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006852 1, curpx, &rs->rules, file, line, &errmsg);
6853 if (!chk) {
6854 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6855 goto error;
6856 }
6857 chk->index = index++;
6858 LIST_ADDQ(&rs->rules, &chk->list);
6859 }
6860
6861 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006862 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006863 if (!chk) {
6864 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6865 goto error;
6866 }
6867 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6868 chk->index = index++;
6869 LIST_ADDQ(&rs->rules, &chk->list);
6870
6871 if (mysql_req) {
6872 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006873 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006874 if (!chk) {
6875 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6876 goto error;
6877 }
6878 chk->expect.custom = tcpcheck_mysql_expect_ok;
6879 chk->index = index++;
6880 LIST_ADDQ(&rs->rules, &chk->list);
6881 }
6882
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006883 ruleset_found:
6884 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006885 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006886
6887 out:
6888 free(errmsg);
6889 return err_code;
6890
6891 error:
6892 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006893 free(user);
6894 free(var);
6895 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006896 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006897 err_code |= ERR_ALERT | ERR_FATAL;
6898 goto out;
6899}
6900
Christopher Faulet1997eca2020-04-03 23:13:50 +02006901int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6902 const char *file, int line)
6903{
6904 static char *ldap_req = "300C020101600702010304008000";
6905
6906 struct tcpcheck_ruleset *rs = NULL;
6907 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6908 struct tcpcheck_rule *chk;
6909 char *errmsg = NULL;
6910 int err_code = 0;
6911
6912 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6913 err_code |= ERR_WARN;
6914
6915 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6916 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006917
6918 curpx->options2 &= ~PR_O2_CHK_ANY;
6919 curpx->options2 |= PR_O2_TCPCHK_CHK;
6920
6921 free_tcpcheck_vars(&rules->preset_vars);
6922 rules->list = NULL;
6923 rules->flags = 0;
6924
Christopher Faulet61cc8522020-04-20 14:54:42 +02006925 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006926 if (rs)
6927 goto ruleset_found;
6928
Christopher Faulet61cc8522020-04-20 14:54:42 +02006929 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006930 if (rs == NULL) {
6931 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6932 goto error;
6933 }
6934
6935 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6936 1, curpx, &rs->rules, file, line, &errmsg);
6937 if (!chk) {
6938 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6939 goto error;
6940 }
6941 chk->index = 0;
6942 LIST_ADDQ(&rs->rules, &chk->list);
6943
6944 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6945 "min-recv", "14",
6946 "on-error", "Not LDAPv3 protocol",
6947 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006948 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006949 if (!chk) {
6950 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6951 goto error;
6952 }
6953 chk->index = 1;
6954 LIST_ADDQ(&rs->rules, &chk->list);
6955
6956 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006957 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006958 if (!chk) {
6959 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6960 goto error;
6961 }
6962 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6963 chk->index = 2;
6964 LIST_ADDQ(&rs->rules, &chk->list);
6965
Christopher Faulet1997eca2020-04-03 23:13:50 +02006966 ruleset_found:
6967 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006968 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006969
6970 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006971 free(errmsg);
6972 return err_code;
6973
6974 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006975 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006976 err_code |= ERR_ALERT | ERR_FATAL;
6977 goto out;
6978}
6979
6980int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6981 const char *file, int line)
6982{
6983 struct tcpcheck_ruleset *rs = NULL;
6984 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6985 struct tcpcheck_rule *chk;
6986 char *spop_req = NULL;
6987 char *errmsg = NULL;
6988 int spop_len = 0, err_code = 0;
6989
6990 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6991 err_code |= ERR_WARN;
6992
6993 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6994 goto out;
6995
Christopher Faulet267b01b2020-04-04 10:27:09 +02006996 curpx->options2 &= ~PR_O2_CHK_ANY;
6997 curpx->options2 |= PR_O2_TCPCHK_CHK;
6998
6999 free_tcpcheck_vars(&rules->preset_vars);
7000 rules->list = NULL;
7001 rules->flags = 0;
7002
7003
Christopher Faulet61cc8522020-04-20 14:54:42 +02007004 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007005 if (rs)
7006 goto ruleset_found;
7007
Christopher Faulet61cc8522020-04-20 14:54:42 +02007008 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007009 if (rs == NULL) {
7010 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7011 goto error;
7012 }
7013
7014 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7015 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7016 goto error;
7017 }
7018 chunk_reset(&trash);
7019 dump_binary(&trash, spop_req, spop_len);
7020 trash.area[trash.data] = '\0';
7021
7022 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7023 1, curpx, &rs->rules, file, line, &errmsg);
7024 if (!chk) {
7025 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7026 goto error;
7027 }
7028 chk->index = 0;
7029 LIST_ADDQ(&rs->rules, &chk->list);
7030
7031 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007032 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007033 if (!chk) {
7034 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7035 goto error;
7036 }
7037 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7038 chk->index = 1;
7039 LIST_ADDQ(&rs->rules, &chk->list);
7040
Christopher Faulet267b01b2020-04-04 10:27:09 +02007041 ruleset_found:
7042 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007043 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007044
7045 out:
7046 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007047 free(errmsg);
7048 return err_code;
7049
7050 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007051 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007052 err_code |= ERR_ALERT | ERR_FATAL;
7053 goto out;
7054}
Christopher Fauletce355072020-04-02 11:44:39 +02007055
Christopher Faulete5870d82020-04-15 11:32:03 +02007056
7057struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7058{
7059 struct tcpcheck_rule *chk = NULL;
7060 struct tcpcheck_http_hdr *hdr = NULL;
7061 char *meth = NULL, *uri = NULL, *vsn = NULL;
7062 char *hdrs, *body;
7063
7064 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7065 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7066 if (hdrs == body)
7067 hdrs = NULL;
7068 if (hdrs) {
7069 *hdrs = '\0';
7070 hdrs +=2;
7071 }
7072 if (body) {
7073 *body = '\0';
7074 body += 4;
7075 }
7076 if (hdrs || body) {
7077 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7078 " Please, consider to use 'http-check send' directive instead.");
7079 }
7080
7081 chk = calloc(1, sizeof(*chk));
7082 if (!chk) {
7083 memprintf(errmsg, "out of memory");
7084 goto error;
7085 }
7086 chk->action = TCPCHK_ACT_SEND;
7087 chk->send.type = TCPCHK_SEND_HTTP;
7088 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7089 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7090 LIST_INIT(&chk->send.http.hdrs);
7091
7092 /* Copy the method, uri and version */
7093 if (*args[cur_arg]) {
7094 if (!*args[cur_arg+1])
7095 uri = args[cur_arg];
7096 else
7097 meth = args[cur_arg];
7098 }
7099 if (*args[cur_arg+1])
7100 uri = args[cur_arg+1];
7101 if (*args[cur_arg+2])
7102 vsn = args[cur_arg+2];
7103
7104 if (meth) {
7105 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7106 chk->send.http.meth.str.area = strdup(meth);
7107 chk->send.http.meth.str.data = strlen(meth);
7108 if (!chk->send.http.meth.str.area) {
7109 memprintf(errmsg, "out of memory");
7110 goto error;
7111 }
7112 }
7113 if (uri) {
7114 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007115 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007116 memprintf(errmsg, "out of memory");
7117 goto error;
7118 }
7119 }
7120 if (vsn) {
7121 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007122 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007123 memprintf(errmsg, "out of memory");
7124 goto error;
7125 }
7126 }
7127
7128 /* Copy the header */
7129 if (hdrs) {
7130 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7131 struct h1m h1m;
7132 int i, ret;
7133
7134 /* Build and parse the request */
7135 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7136
7137 h1m.flags = H1_MF_HDRS_ONLY;
7138 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7139 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7140 &h1m, NULL);
7141 if (ret <= 0) {
7142 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7143 goto error;
7144 }
7145
Christopher Fauletb61caf42020-04-21 10:57:42 +02007146 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007147 hdr = calloc(1, sizeof(*hdr));
7148 if (!hdr) {
7149 memprintf(errmsg, "out of memory");
7150 goto error;
7151 }
7152 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007153 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007154 if (!hdr->name.ptr) {
7155 memprintf(errmsg, "out of memory");
7156 goto error;
7157 }
7158
Christopher Fauletb61caf42020-04-21 10:57:42 +02007159 ist0(tmp_hdrs[i].v);
7160 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 +02007161 goto error;
7162 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7163 }
7164 }
7165
7166 /* Copy the body */
7167 if (body) {
7168 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007169 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007170 memprintf(errmsg, "out of memory");
7171 goto error;
7172 }
7173 }
7174
7175 return chk;
7176
7177 error:
7178 free_tcpcheck_http_hdr(hdr);
7179 free_tcpcheck(chk, 0);
7180 return NULL;
7181}
7182
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007183int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7184 const char *file, int line)
7185{
Christopher Faulete5870d82020-04-15 11:32:03 +02007186 struct tcpcheck_ruleset *rs = NULL;
7187 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7188 struct tcpcheck_rule *chk;
7189 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007190 int err_code = 0;
7191
7192 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7193 err_code |= ERR_WARN;
7194
7195 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7196 goto out;
7197
Christopher Faulete5870d82020-04-15 11:32:03 +02007198 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7199 if (!chk) {
7200 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7201 goto error;
7202 }
7203 if (errmsg) {
7204 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7205 err_code |= ERR_WARN;
7206 free(errmsg);
7207 errmsg = NULL;
7208 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007209
Christopher Faulete5870d82020-04-15 11:32:03 +02007210 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007211 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007212 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007213
Christopher Faulete5870d82020-04-15 11:32:03 +02007214 free_tcpcheck_vars(&rules->preset_vars);
7215 rules->list = NULL;
7216 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007217
Christopher Faulete5870d82020-04-15 11:32:03 +02007218 /* Deduce the ruleset name from the proxy info */
7219 chunk_printf(&trash, "*http-check-%s_%s-%d",
7220 ((curpx == defpx) ? "defaults" : curpx->id),
7221 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007222
Christopher Faulet61cc8522020-04-20 14:54:42 +02007223 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007224 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007225 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007226 if (rs == NULL) {
7227 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7228 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007229 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007230 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007231
Christopher Faulete5870d82020-04-15 11:32:03 +02007232 rules->list = &rs->rules;
7233 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7234 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7235 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7236 rules->list = NULL;
7237 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007238 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007239
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007240 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007241 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007242 return err_code;
7243
7244 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007245 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007246 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007247 err_code |= ERR_ALERT | ERR_FATAL;
7248 goto out;
7249}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007250
Christopher Faulet6f557912020-04-09 15:58:50 +02007251int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7252 const char *file, int line)
7253{
7254 int err_code = 0;
7255
Christopher Faulet6f557912020-04-09 15:58:50 +02007256 curpx->options2 &= ~PR_O2_CHK_ANY;
7257 curpx->options2 |= PR_O2_EXT_CHK;
7258 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7259 goto out;
7260
7261 out:
7262 return err_code;
7263}
7264
Christopher Fauletce8111e2020-04-06 15:04:11 +02007265/* Parse the "addr" server keyword */
7266static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7267 char **errmsg)
7268{
7269 struct sockaddr_storage *sk;
7270 struct protocol *proto;
7271 int port1, port2, err_code = 0;
7272
7273
7274 if (!*args[*cur_arg+1]) {
7275 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7276 goto error;
7277 }
7278
7279 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7280 if (!sk) {
7281 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7282 goto error;
7283 }
7284
7285 proto = protocol_by_family(sk->ss_family);
7286 if (!proto || !proto->connect) {
7287 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7288 args[*cur_arg], args[*cur_arg+1]);
7289 goto error;
7290 }
7291
7292 if (port1 != port2) {
7293 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7294 args[*cur_arg], args[*cur_arg+1]);
7295 goto error;
7296 }
7297
7298 srv->check.addr = srv->agent.addr = *sk;
7299 srv->flags |= SRV_F_CHECKADDR;
7300 srv->flags |= SRV_F_AGENTADDR;
7301
7302 out:
7303 return err_code;
7304
7305 error:
7306 err_code |= ERR_ALERT | ERR_FATAL;
7307 goto out;
7308}
7309
7310
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007311/* Parse the "agent-addr" server keyword */
7312static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7313 char **errmsg)
7314{
7315 int err_code = 0;
7316
7317 if (!*(args[*cur_arg+1])) {
7318 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7319 goto error;
7320 }
7321 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7322 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7323 goto error;
7324 }
7325
7326 out:
7327 return err_code;
7328
7329 error:
7330 err_code |= ERR_ALERT | ERR_FATAL;
7331 goto out;
7332}
7333
7334/* Parse the "agent-check" server keyword */
7335static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7336 char **errmsg)
7337{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007338 struct tcpcheck_ruleset *rs = NULL;
7339 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7340 struct tcpcheck_rule *chk;
7341 int err_code = 0;
7342
7343 if (srv->do_agent)
7344 goto out;
7345
7346 if (!rules) {
7347 rules = calloc(1, sizeof(*rules));
7348 if (!rules) {
7349 memprintf(errmsg, "out of memory.");
7350 goto error;
7351 }
7352 LIST_INIT(&rules->preset_vars);
7353 srv->agent.tcpcheck_rules = rules;
7354 }
7355 rules->list = NULL;
7356 rules->flags = 0;
7357
Christopher Faulet61cc8522020-04-20 14:54:42 +02007358 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007359 if (rs)
7360 goto ruleset_found;
7361
Christopher Faulet61cc8522020-04-20 14:54:42 +02007362 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007363 if (rs == NULL) {
7364 memprintf(errmsg, "out of memory.");
7365 goto error;
7366 }
7367
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007368 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007369 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7370 if (!chk) {
7371 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7372 goto error;
7373 }
7374 chk->index = 0;
7375 LIST_ADDQ(&rs->rules, &chk->list);
7376
7377 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007378 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7379 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007380 if (!chk) {
7381 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7382 goto error;
7383 }
7384 chk->expect.custom = tcpcheck_agent_expect_reply;
7385 chk->index = 1;
7386 LIST_ADDQ(&rs->rules, &chk->list);
7387
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007388 ruleset_found:
7389 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007390 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007391 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007392
7393 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007394 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007395
7396 error:
7397 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007398 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007399 err_code |= ERR_ALERT | ERR_FATAL;
7400 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007401}
7402
7403/* Parse the "agent-inter" server keyword */
7404static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7405 char **errmsg)
7406{
7407 const char *err = NULL;
7408 unsigned int delay;
7409 int err_code = 0;
7410
7411 if (!*(args[*cur_arg+1])) {
7412 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7413 goto error;
7414 }
7415
7416 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7417 if (err == PARSE_TIME_OVER) {
7418 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7419 args[*cur_arg+1], args[*cur_arg], srv->id);
7420 goto error;
7421 }
7422 else if (err == PARSE_TIME_UNDER) {
7423 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7424 args[*cur_arg+1], args[*cur_arg], srv->id);
7425 goto error;
7426 }
7427 else if (err) {
7428 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7429 *err, srv->id);
7430 goto error;
7431 }
7432 if (delay <= 0) {
7433 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7434 delay, args[*cur_arg], srv->id);
7435 goto error;
7436 }
7437 srv->agent.inter = delay;
7438
7439 out:
7440 return err_code;
7441
7442 error:
7443 err_code |= ERR_ALERT | ERR_FATAL;
7444 goto out;
7445}
7446
7447/* Parse the "agent-port" server keyword */
7448static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7449 char **errmsg)
7450{
7451 int err_code = 0;
7452
7453 if (!*(args[*cur_arg+1])) {
7454 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7455 goto error;
7456 }
7457
7458 global.maxsock++;
7459 srv->agent.port = atol(args[*cur_arg+1]);
7460
7461 out:
7462 return err_code;
7463
7464 error:
7465 err_code |= ERR_ALERT | ERR_FATAL;
7466 goto out;
7467}
7468
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007469int set_srv_agent_send(struct server *srv, const char *send)
7470{
7471 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7472 struct tcpcheck_var *var = NULL;
7473 char *str;
7474
7475 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007476 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007477 if (str == NULL || var == NULL)
7478 goto error;
7479
7480 free_tcpcheck_vars(&rules->preset_vars);
7481
7482 var->data.type = SMP_T_STR;
7483 var->data.u.str.area = str;
7484 var->data.u.str.data = strlen(str);
7485 LIST_INIT(&var->list);
7486 LIST_ADDQ(&rules->preset_vars, &var->list);
7487
7488 return 1;
7489
7490 error:
7491 free(str);
7492 free(var);
7493 return 0;
7494}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007495
7496/* Parse the "agent-send" server keyword */
7497static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7498 char **errmsg)
7499{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007500 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007501 int err_code = 0;
7502
7503 if (!*(args[*cur_arg+1])) {
7504 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7505 goto error;
7506 }
7507
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007508 if (!rules) {
7509 rules = calloc(1, sizeof(*rules));
7510 if (!rules) {
7511 memprintf(errmsg, "out of memory.");
7512 goto error;
7513 }
7514 LIST_INIT(&rules->preset_vars);
7515 srv->agent.tcpcheck_rules = rules;
7516 }
7517
7518 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007519 memprintf(errmsg, "out of memory.");
7520 goto error;
7521 }
7522
7523 out:
7524 return err_code;
7525
7526 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007527 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007528 err_code |= ERR_ALERT | ERR_FATAL;
7529 goto out;
7530}
7531
7532/* Parse the "no-agent-send" server keyword */
7533static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7534 char **errmsg)
7535{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007536 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007537 return 0;
7538}
7539
Christopher Fauletce8111e2020-04-06 15:04:11 +02007540/* Parse the "check" server keyword */
7541static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7542 char **errmsg)
7543{
7544 srv->do_check = 1;
7545 return 0;
7546}
7547
7548/* Parse the "check-send-proxy" server keyword */
7549static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7550 char **errmsg)
7551{
7552 srv->check.send_proxy = 1;
7553 return 0;
7554}
7555
7556/* Parse the "check-via-socks4" server keyword */
7557static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7558 char **errmsg)
7559{
7560 srv->check.via_socks4 = 1;
7561 return 0;
7562}
7563
7564/* Parse the "no-check" server keyword */
7565static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7566 char **errmsg)
7567{
7568 deinit_srv_check(srv);
7569 return 0;
7570}
7571
7572/* Parse the "no-check-send-proxy" server keyword */
7573static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7574 char **errmsg)
7575{
7576 srv->check.send_proxy = 0;
7577 return 0;
7578}
7579
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007580/* parse the "check-proto" server keyword */
7581static int srv_parse_check_proto(char **args, int *cur_arg,
7582 struct proxy *px, struct server *newsrv, char **err)
7583{
7584 int err_code = 0;
7585
7586 if (!*args[*cur_arg + 1]) {
7587 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7588 goto error;
7589 }
7590 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7591 if (!newsrv->check.mux_proto) {
7592 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7593 goto error;
7594 }
7595
7596 out:
7597 return err_code;
7598
7599 error:
7600 err_code |= ERR_ALERT | ERR_FATAL;
7601 goto out;
7602}
7603
7604
Christopher Fauletce8111e2020-04-06 15:04:11 +02007605/* Parse the "rise" server keyword */
7606static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7607 char **errmsg)
7608{
7609 int err_code = 0;
7610
7611 if (!*args[*cur_arg + 1]) {
7612 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7613 goto error;
7614 }
7615
7616 srv->check.rise = atol(args[*cur_arg+1]);
7617 if (srv->check.rise <= 0) {
7618 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7619 goto error;
7620 }
7621
7622 if (srv->check.health)
7623 srv->check.health = srv->check.rise;
7624
7625 out:
7626 return err_code;
7627
7628 error:
7629 deinit_srv_agent_check(srv);
7630 err_code |= ERR_ALERT | ERR_FATAL;
7631 goto out;
7632 return 0;
7633}
7634
7635/* Parse the "fall" server keyword */
7636static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7637 char **errmsg)
7638{
7639 int err_code = 0;
7640
7641 if (!*args[*cur_arg + 1]) {
7642 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7643 goto error;
7644 }
7645
7646 srv->check.fall = atol(args[*cur_arg+1]);
7647 if (srv->check.fall <= 0) {
7648 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7649 goto error;
7650 }
7651
7652 out:
7653 return err_code;
7654
7655 error:
7656 deinit_srv_agent_check(srv);
7657 err_code |= ERR_ALERT | ERR_FATAL;
7658 goto out;
7659 return 0;
7660}
7661
7662/* Parse the "inter" server keyword */
7663static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7664 char **errmsg)
7665{
7666 const char *err = NULL;
7667 unsigned int delay;
7668 int err_code = 0;
7669
7670 if (!*(args[*cur_arg+1])) {
7671 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7672 goto error;
7673 }
7674
7675 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7676 if (err == PARSE_TIME_OVER) {
7677 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7678 args[*cur_arg+1], args[*cur_arg], srv->id);
7679 goto error;
7680 }
7681 else if (err == PARSE_TIME_UNDER) {
7682 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7683 args[*cur_arg+1], args[*cur_arg], srv->id);
7684 goto error;
7685 }
7686 else if (err) {
7687 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7688 *err, srv->id);
7689 goto error;
7690 }
7691 if (delay <= 0) {
7692 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7693 delay, args[*cur_arg], srv->id);
7694 goto error;
7695 }
7696 srv->check.inter = delay;
7697
7698 out:
7699 return err_code;
7700
7701 error:
7702 err_code |= ERR_ALERT | ERR_FATAL;
7703 goto out;
7704}
7705
7706
7707/* Parse the "fastinter" server keyword */
7708static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7709 char **errmsg)
7710{
7711 const char *err = NULL;
7712 unsigned int delay;
7713 int err_code = 0;
7714
7715 if (!*(args[*cur_arg+1])) {
7716 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7717 goto error;
7718 }
7719
7720 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7721 if (err == PARSE_TIME_OVER) {
7722 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7723 args[*cur_arg+1], args[*cur_arg], srv->id);
7724 goto error;
7725 }
7726 else if (err == PARSE_TIME_UNDER) {
7727 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7728 args[*cur_arg+1], args[*cur_arg], srv->id);
7729 goto error;
7730 }
7731 else if (err) {
7732 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7733 *err, srv->id);
7734 goto error;
7735 }
7736 if (delay <= 0) {
7737 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7738 delay, args[*cur_arg], srv->id);
7739 goto error;
7740 }
7741 srv->check.fastinter = delay;
7742
7743 out:
7744 return err_code;
7745
7746 error:
7747 err_code |= ERR_ALERT | ERR_FATAL;
7748 goto out;
7749}
7750
7751
7752/* Parse the "downinter" server keyword */
7753static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7754 char **errmsg)
7755{
7756 const char *err = NULL;
7757 unsigned int delay;
7758 int err_code = 0;
7759
7760 if (!*(args[*cur_arg+1])) {
7761 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7762 goto error;
7763 }
7764
7765 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7766 if (err == PARSE_TIME_OVER) {
7767 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7768 args[*cur_arg+1], args[*cur_arg], srv->id);
7769 goto error;
7770 }
7771 else if (err == PARSE_TIME_UNDER) {
7772 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7773 args[*cur_arg+1], args[*cur_arg], srv->id);
7774 goto error;
7775 }
7776 else if (err) {
7777 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7778 *err, srv->id);
7779 goto error;
7780 }
7781 if (delay <= 0) {
7782 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7783 delay, args[*cur_arg], srv->id);
7784 goto error;
7785 }
7786 srv->check.downinter = delay;
7787
7788 out:
7789 return err_code;
7790
7791 error:
7792 err_code |= ERR_ALERT | ERR_FATAL;
7793 goto out;
7794}
7795
7796/* Parse the "port" server keyword */
7797static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7798 char **errmsg)
7799{
7800 int err_code = 0;
7801
7802 if (!*(args[*cur_arg+1])) {
7803 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7804 goto error;
7805 }
7806
7807 global.maxsock++;
7808 srv->check.port = atol(args[*cur_arg+1]);
7809 srv->flags |= SRV_F_CHECKPORT;
7810
7811 out:
7812 return err_code;
7813
7814 error:
7815 err_code |= ERR_ALERT | ERR_FATAL;
7816 goto out;
7817}
7818
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007819static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007820 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7821 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7822 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007823 { 0, NULL, NULL },
7824}};
7825
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007826static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007827 { "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 +02007828 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7829 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7830 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7831 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7832 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007833 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007834 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007835 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7836 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007837 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007838 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7839 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7840 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7841 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7842 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7843 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7844 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7845 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007846 { NULL, NULL, 0 },
7847}};
7848
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007849INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007850INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007851
Willy Tarreaubd741542010-03-16 18:46:54 +01007852/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007853 * Local variables:
7854 * c-indent-level: 8
7855 * c-basic-offset: 8
7856 * End:
7857 */