blob: 279e8af01b65cd4c26c819ea48a2cca949eb5e7f [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 Tarreau122eba92020-06-04 10:15:32 +020033#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020034#include <haproxy/api.h>
Christopher Fauletfd6c2292020-03-25 18:20:15 +010035#include <common/cfgparse.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020036#include <haproxy/chunk.h>
Willy Tarreau2741c8c2020-06-02 11:28:02 +020037#include <haproxy/istbuf.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020038#include <haproxy/list.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020039#include <haproxy/regex.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020040#include <haproxy/tools.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020041#include <haproxy/time.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020042#include <haproxy/thread.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020043#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020044#include <haproxy/http_htx.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020045#include <haproxy/h1.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020046#include <haproxy/htx.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020047
48#include <types/global.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020049#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010050#include <types/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020051
Christopher Fauletba3c68f2020-04-01 16:27:05 +020052#include <proto/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020053#include <proto/backend.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020054#include <proto/checks.h>
William Lallemand9ed62032016-11-21 17:49:11 +010055#include <proto/stats.h>
Willy Tarreau0f6ffd62020-06-03 19:33:00 +020056#include <haproxy/fd.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020057#include <proto/log.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020058#include <proto/mux_pt.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020059#include <proto/queue.h>
Willy Tarreaufc8f6a82020-06-03 19:20:59 +020060#include <haproxy/port_range.h>
Willy Tarreaue8c66af2008-01-13 18:40:14 +010061#include <proto/proto_tcp.h>
Willy Tarreau2dd7c352020-06-03 15:26:55 +020062#include <haproxy/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010063#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020064#include <proto/server.h>
Willy Tarreau48d6bf22016-06-21 16:27:34 +020065#include <proto/signal.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010066#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020067#include <proto/task.h>
Gaetan Rivet13a50432020-02-21 18:13:44 +010068#include <proto/vars.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020069#include <proto/log.h>
70#include <proto/dns.h>
Willy Tarreau832ce652020-06-04 08:36:05 +020071#include <haproxy/proto_udp.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020072#include <proto/ssl_sock.h>
Christopher Fauletb7d30092020-03-30 15:19:03 +020073#include <proto/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020074
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020075static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010076
Christopher Faulet61cc8522020-04-20 14:54:42 +020077static int wake_srv_chk(struct conn_stream *cs);
78struct data_cb check_conn_cb = {
79 .wake = wake_srv_chk,
80 .name = "CHCK",
81};
Christopher Fauletd7e63962020-04-17 20:15:59 +020082
Christopher Fauletd7cee712020-04-21 13:45:00 +020083/* Global tree to share all tcp-checks */
84struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020085
86
Willy Tarreau8ceae722018-11-26 11:58:30 +010087DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
88DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020089
Gaetan Rivet05d692d2020-02-14 17:42:54 +010090/* Dummy frontend used to create all checks sessions. */
91static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020092
Christopher Faulet61cc8522020-04-20 14:54:42 +020093/**************************************************************************/
94/************************ Handle check results ****************************/
95/**************************************************************************/
96struct check_status {
97 short result; /* one of SRV_CHK_* */
98 char *info; /* human readable short info */
99 char *desc; /* long description */
100};
101
102struct analyze_status {
103 char *desc; /* description */
104 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
105};
106
Simon Horman63a4a822012-03-19 07:24:41 +0900107static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100108 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
109 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200110 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200111
Willy Tarreau23964182014-05-20 20:56:30 +0200112 /* Below we have finished checks */
113 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100114 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100115
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100116 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200117
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100118 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
119 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
120 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200121
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100122 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
123 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
124 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200125
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100126 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
127 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200128
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200129 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200130
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100131 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
132 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
133 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900134
135 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
136 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200137 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200138};
139
Simon Horman63a4a822012-03-19 07:24:41 +0900140static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100141 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
142
143 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
144 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
145
146 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
147 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
148 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
149 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
150
151 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
152 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
153 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
154};
155
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100156/* checks if <err> is a real error for errno or one that can be ignored, and
157 * return 0 for these ones or <err> for real ones.
158 */
159static inline int unclean_errno(int err)
160{
161 if (err == EAGAIN || err == EINPROGRESS ||
162 err == EISCONN || err == EALREADY)
163 return 0;
164 return err;
165}
166
Christopher Faulet61cc8522020-04-20 14:54:42 +0200167/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200168const char *get_check_status_description(short check_status) {
169
170 const char *desc;
171
172 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200173 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200174 else
175 desc = NULL;
176
177 if (desc && *desc)
178 return desc;
179 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200180 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200181}
182
Christopher Faulet61cc8522020-04-20 14:54:42 +0200183/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200184const char *get_check_status_info(short check_status) {
185
186 const char *info;
187
188 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200189 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200190 else
191 info = NULL;
192
193 if (info && *info)
194 return info;
195 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200196 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200197}
198
Christopher Faulet61cc8522020-04-20 14:54:42 +0200199/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100200const char *get_analyze_status(short analyze_status) {
201
202 const char *desc;
203
204 if (analyze_status < HANA_STATUS_SIZE)
205 desc = analyze_statuses[analyze_status].desc;
206 else
207 desc = NULL;
208
209 if (desc && *desc)
210 return desc;
211 else
212 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
213}
214
Christopher Faulet61cc8522020-04-20 14:54:42 +0200215/* Sets check->status, update check->duration and fill check->result with an
216 * adequate CHK_RES_* value. The new check->health is computed based on the
217 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200218 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200219 * Shows information in logs about failed health check if server is UP or
220 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200221 */
Simon Horman4a741432013-02-23 15:35:38 +0900222static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100223{
Simon Horman4a741432013-02-23 15:35:38 +0900224 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200225 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200226 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900227
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200228 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100229 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900230 check->desc[0] = '\0';
231 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200232 return;
233 }
234
Simon Horman4a741432013-02-23 15:35:38 +0900235 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200236 return;
237
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200238 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900239 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
240 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200241 } else
Simon Horman4a741432013-02-23 15:35:38 +0900242 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200243
Simon Horman4a741432013-02-23 15:35:38 +0900244 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200245 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900246 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200247
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100248 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900249 check->duration = -1;
250 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200251 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900252 check->duration = tv_ms_elapsed(&check->start, &now);
253 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200254 }
255
Willy Tarreau23964182014-05-20 20:56:30 +0200256 /* no change is expected if no state change occurred */
257 if (check->result == CHK_RES_NEUTRAL)
258 return;
259
Olivier Houchard0923fa42019-01-11 18:43:04 +0100260 /* If the check was really just sending a mail, it won't have an
261 * associated server, so we're done now.
262 */
263 if (!s)
264 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200265 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200266
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200267 switch (check->result) {
268 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200269 /* Failure to connect to the agent as a secondary check should not
270 * cause the server to be marked down.
271 */
272 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900273 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200274 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100275 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200276 report = 1;
277 check->health--;
278 if (check->health < check->rise)
279 check->health = 0;
280 }
281 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200282
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200283 case CHK_RES_PASSED:
284 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
285 if ((check->health < check->rise + check->fall - 1) &&
286 (check->result == CHK_RES_PASSED || check->health > 0)) {
287 report = 1;
288 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200289
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200290 if (check->health >= check->rise)
291 check->health = check->rise + check->fall - 1; /* OK now */
292 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200293
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200294 /* clear consecutive_errors if observing is enabled */
295 if (s->onerror)
296 s->consecutive_errors = 0;
297 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100298
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200299 default:
300 break;
301 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200302
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200303 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
304 (status != prev_status || report)) {
305 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200306 "%s check for %sserver %s/%s %s%s",
307 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200308 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100309 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100310 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200311 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200312
Emeric Brun5a133512017-10-19 14:42:30 +0200313 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200314
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100315 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200316 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
317 (check->health >= check->rise) ? check->fall : check->rise,
318 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200319
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200320 ha_warning("%s.\n", trash.area);
321 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
322 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200323 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200324}
325
Willy Tarreau4eec5472014-05-20 22:32:27 +0200326/* Marks the check <check>'s server down if the current check is already failed
327 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200328 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200329static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200330{
Simon Horman4a741432013-02-23 15:35:38 +0900331 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900332
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200333 /* The agent secondary check should only cause a server to be marked
334 * as down if check->status is HCHK_STATUS_L7STS, which indicates
335 * that the agent returned "fail", "stopped" or "down".
336 * The implication here is that failure to connect to the agent
337 * as a secondary check should not cause the server to be marked
338 * down. */
339 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
340 return;
341
Willy Tarreau4eec5472014-05-20 22:32:27 +0200342 if (check->health > 0)
343 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100344
Willy Tarreau4eec5472014-05-20 22:32:27 +0200345 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200346 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200347}
348
Willy Tarreauaf549582014-05-16 17:37:50 +0200349/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200350 * it isn't in maintenance, it is not tracking a down server and other checks
351 * comply. The rule is simple : by default, a server is up, unless any of the
352 * following conditions is true :
353 * - health check failed (check->health < rise)
354 * - agent check failed (agent->health < rise)
355 * - the server tracks a down server (track && track->state == STOPPED)
356 * Note that if the server has a slowstart, it will switch to STARTING instead
357 * of RUNNING. Also, only the health checks support the nolb mode, so the
358 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200359 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200360static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200361{
Simon Horman4a741432013-02-23 15:35:38 +0900362 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100363
Emeric Brun52a91d32017-08-31 14:41:55 +0200364 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200365 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100366
Emeric Brun52a91d32017-08-31 14:41:55 +0200367 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200368 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100369
Willy Tarreau3e048382014-05-21 10:30:54 +0200370 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
371 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100372
Willy Tarreau3e048382014-05-21 10:30:54 +0200373 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
374 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200375
Emeric Brun52a91d32017-08-31 14:41:55 +0200376 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200377 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100378
Emeric Brun5a133512017-10-19 14:42:30 +0200379 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100380}
381
Willy Tarreaudb58b792014-05-21 13:57:23 +0200382/* Marks the check <check> as valid and tries to set its server into stopping mode
383 * if it was running or starting, and provided it isn't in maintenance and other
384 * checks comply. The conditions for the server to be marked in stopping mode are
385 * the same as for it to be turned up. Also, only the health checks support the
386 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200387 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200388static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200389{
Simon Horman4a741432013-02-23 15:35:38 +0900390 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100391
Emeric Brun52a91d32017-08-31 14:41:55 +0200392 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200393 return;
394
Willy Tarreaudb58b792014-05-21 13:57:23 +0200395 if (check->state & CHK_ST_AGENT)
396 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100397
Emeric Brun52a91d32017-08-31 14:41:55 +0200398 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200399 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100400
Willy Tarreaudb58b792014-05-21 13:57:23 +0200401 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
402 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100403
Willy Tarreaudb58b792014-05-21 13:57:23 +0200404 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
405 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100406
Willy Tarreaub26881a2017-12-23 11:16:49 +0100407 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100408}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200409
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100410/* note: use health_adjust() only, which first checks that the observe mode is
411 * enabled.
412 */
413void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100414{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100415 int failed;
416 int expire;
417
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100418 if (s->observe >= HANA_OBS_SIZE)
419 return;
420
Willy Tarreaubb956662013-01-24 00:37:39 +0100421 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100422 return;
423
424 switch (analyze_statuses[status].lr[s->observe - 1]) {
425 case 1:
426 failed = 1;
427 break;
428
429 case 2:
430 failed = 0;
431 break;
432
433 default:
434 return;
435 }
436
437 if (!failed) {
438 /* good: clear consecutive_errors */
439 s->consecutive_errors = 0;
440 return;
441 }
442
Olivier Houchard7059c552019-03-08 18:49:32 +0100443 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100444
445 if (s->consecutive_errors < s->consecutive_errors_limit)
446 return;
447
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100448 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
449 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100450
451 switch (s->onerror) {
452 case HANA_ONERR_FASTINTER:
453 /* force fastinter - nothing to do here as all modes force it */
454 break;
455
456 case HANA_ONERR_SUDDTH:
457 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900458 if (s->check.health > s->check.rise)
459 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100460
461 /* no break - fall through */
462
463 case HANA_ONERR_FAILCHK:
464 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200465 set_server_check_status(&s->check, HCHK_STATUS_HANA,
466 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200467 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100468 break;
469
470 case HANA_ONERR_MARKDWN:
471 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900472 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200473 set_server_check_status(&s->check, HCHK_STATUS_HANA,
474 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200475 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100476 break;
477
478 default:
479 /* write a warning? */
480 break;
481 }
482
483 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100484 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100485
Simon Horman66183002013-02-23 10:16:43 +0900486 if (s->check.fastinter) {
487 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300488 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200489 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300490 /* requeue check task with new expire */
491 task_queue(s->check.task);
492 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100493 }
Willy Tarreauef781042010-01-27 11:53:01 +0100494}
495
Christopher Faulet61cc8522020-04-20 14:54:42 +0200496/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100497 * closed, keep errno intact as it is supposed to contain the valid error code.
498 * If no error is reported, check the socket's error queue using getsockopt().
499 * Warning, this must be done only once when returning from poll, and never
500 * after an I/O error was attempted, otherwise the error queue might contain
501 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
502 * socket. Returns non-zero if an error was reported, zero if everything is
503 * clean (including a properly closed socket).
504 */
505static int retrieve_errno_from_socket(struct connection *conn)
506{
507 int skerr;
508 socklen_t lskerr = sizeof(skerr);
509
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100510 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100511 return 1;
512
Willy Tarreau3c728722014-01-23 13:50:42 +0100513 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100514 return 0;
515
Willy Tarreau585744b2017-08-24 14:31:19 +0200516 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100517 errno = skerr;
518
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100519 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100520
521 if (!errno) {
522 /* we could not retrieve an error, that does not mean there is
523 * none. Just don't change anything and only report the prior
524 * error if any.
525 */
526 if (conn->flags & CO_FL_ERROR)
527 return 1;
528 else
529 return 0;
530 }
531
532 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
533 return 1;
534}
535
Christopher Faulet61cc8522020-04-20 14:54:42 +0200536/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100537 * and adjust the server status accordingly. It may make use of <errno_bck>
538 * if non-null when the caller is absolutely certain of its validity (eg:
539 * checked just after a syscall). If the caller doesn't have a valid errno,
540 * it can pass zero, and retrieve_errno_from_socket() will be called to try
541 * to extract errno from the socket. If no error is reported, it will consider
542 * the <expired> flag. This is intended to be used when a connection error was
543 * reported in conn->flags or when a timeout was reported in <expired>. The
544 * function takes care of not updating a server status which was already set.
545 * All situations where at least one of <expired> or CO_FL_ERROR are set
546 * produce a status.
547 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200548static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100549{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200550 struct conn_stream *cs = check->cs;
551 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100552 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200553 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200554 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100555
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100556 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100557 return;
558
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100559 errno = unclean_errno(errno_bck);
560 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100561 retrieve_errno_from_socket(conn);
562
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200563 if (conn && !(conn->flags & CO_FL_ERROR) &&
564 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100565 return;
566
567 /* we'll try to build a meaningful error message depending on the
568 * context of the error possibly present in conn->err_code, and the
569 * socket error possibly collected above. This is useful to know the
570 * exact step of the L6 layer (eg: SSL handshake).
571 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200572 chk = get_trash_chunk();
573
Christopher Faulet799f3a42020-04-07 12:06:14 +0200574 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200575 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200576 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200577 if (!step)
578 chunk_printf(chk, " at initial connection step of tcp-check");
579 else {
580 chunk_printf(chk, " at step %d of tcp-check", step);
581 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200582 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
583 if (check->current_step->connect.port)
584 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200585 else
586 chunk_appendf(chk, " (connect)");
587 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200588 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
589 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100590
591 switch (expect->type) {
592 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200593 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100594 break;
595 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200596 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100597 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200598 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200599 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100600 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200601 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100602 chunk_appendf(chk, " (expect binary regex)");
603 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200604 case TCPCHK_EXPECT_STRING_LF:
605 chunk_appendf(chk, " (expect log-format string)");
606 break;
607 case TCPCHK_EXPECT_BINARY_LF:
608 chunk_appendf(chk, " (expect log-format binary)");
609 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200610 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200611 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200612 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200613 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200614 chunk_appendf(chk, " (expect HTTP status regex)");
615 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200616 case TCPCHK_EXPECT_HTTP_HEADER:
617 chunk_appendf(chk, " (expect HTTP header pattern)");
618 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200619 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200620 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200621 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200622 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200623 chunk_appendf(chk, " (expect HTTP body regex)");
624 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200625 case TCPCHK_EXPECT_HTTP_BODY_LF:
626 chunk_appendf(chk, " (expect log-format HTTP body)");
627 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200628 case TCPCHK_EXPECT_CUSTOM:
629 chunk_appendf(chk, " (expect custom function)");
630 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100631 case TCPCHK_EXPECT_UNDEF:
632 chunk_appendf(chk, " (undefined expect!)");
633 break;
634 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200635 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200636 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200637 chunk_appendf(chk, " (send)");
638 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200639
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200640 if (check->current_step && check->current_step->comment)
641 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200642 }
643 }
644
Willy Tarreau00149122017-10-04 18:05:01 +0200645 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100646 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200647 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
648 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100649 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200650 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
651 chk->area);
652 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100653 }
654 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100655 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200656 chunk_printf(&trash, "%s%s", strerror(errno),
657 chk->area);
658 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100659 }
660 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200661 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100662 }
663 }
664
Willy Tarreau00149122017-10-04 18:05:01 +0200665 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200666 /* NOTE: this is reported after <fall> tries */
667 chunk_printf(chk, "No port available for the TCP connection");
668 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
669 }
670
Willy Tarreau00149122017-10-04 18:05:01 +0200671 if (!conn) {
672 /* connection allocation error before the connection was established */
673 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
674 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100675 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100676 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200677 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100678 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
679 else if (expired)
680 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200681
682 /*
683 * might be due to a server IP change.
684 * Let's trigger a DNS resolution if none are currently running.
685 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100686 if (check->server)
687 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200688
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100689 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100690 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100691 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200692 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100693 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
694 else if (expired)
695 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
696 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200697 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100698 /* I/O error after connection was established and before we could diagnose */
699 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
700 }
701 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200702 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
703
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100704 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200705 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
706 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200707 tout = check->current_step->expect.tout_status;
708 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100709 }
710
711 return;
712}
713
Willy Tarreaubaaee002006-06-26 02:48:02 +0200714
Christopher Faulet61cc8522020-04-20 14:54:42 +0200715/**************************************************************************/
716/*************** Init/deinit tcp-check rules and ruleset ******************/
717/**************************************************************************/
718/* Releases memory allocated for a log-format string */
719static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200720{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200721 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100722
Christopher Faulet61cc8522020-04-20 14:54:42 +0200723 list_for_each_entry_safe(lf, lfb, fmt, list) {
724 LIST_DEL(&lf->list);
725 release_sample_expr(lf->expr);
726 free(lf->arg);
727 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100728 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200729}
730
Christopher Faulet61cc8522020-04-20 14:54:42 +0200731/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
732static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100733{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200734 if (!hdr)
735 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200736
Christopher Faulet61cc8522020-04-20 14:54:42 +0200737 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200738 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200739 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100740}
741
Christopher Faulet61cc8522020-04-20 14:54:42 +0200742/* Releases memory allocated for an HTTP header list used in a tcp-check send
743 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200744 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200745static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200746{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200747 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200748
Christopher Faulet61cc8522020-04-20 14:54:42 +0200749 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
750 LIST_DEL(&hdr->list);
751 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200752 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200753}
754
Christopher Faulet61cc8522020-04-20 14:54:42 +0200755/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
756 * tcp-check was allocated using a memory pool (it is used to instantiate email
757 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200758 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200759static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200760{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200761 if (!rule)
762 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200763
Christopher Faulet61cc8522020-04-20 14:54:42 +0200764 free(rule->comment);
765 switch (rule->action) {
766 case TCPCHK_ACT_SEND:
767 switch (rule->send.type) {
768 case TCPCHK_SEND_STRING:
769 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200770 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200771 break;
772 case TCPCHK_SEND_STRING_LF:
773 case TCPCHK_SEND_BINARY_LF:
774 free_tcpcheck_fmt(&rule->send.fmt);
775 break;
776 case TCPCHK_SEND_HTTP:
777 free(rule->send.http.meth.str.area);
778 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200779 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200780 else
781 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200782 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200783 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
784 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200785 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200786 else
787 free_tcpcheck_fmt(&rule->send.http.body_fmt);
788 break;
789 case TCPCHK_SEND_UNDEF:
790 break;
791 }
792 break;
793 case TCPCHK_ACT_EXPECT:
794 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
795 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
796 release_sample_expr(rule->expect.status_expr);
797 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200798 case TCPCHK_EXPECT_HTTP_STATUS:
799 free(rule->expect.codes.codes);
800 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200801 case TCPCHK_EXPECT_STRING:
802 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200803 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200804 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200805 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200806 case TCPCHK_EXPECT_STRING_REGEX:
807 case TCPCHK_EXPECT_BINARY_REGEX:
808 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
809 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200810 regex_free(rule->expect.regex);
811 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200812 case TCPCHK_EXPECT_STRING_LF:
813 case TCPCHK_EXPECT_BINARY_LF:
814 case TCPCHK_EXPECT_HTTP_BODY_LF:
815 free_tcpcheck_fmt(&rule->expect.fmt);
816 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200817 case TCPCHK_EXPECT_HTTP_HEADER:
818 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
819 regex_free(rule->expect.hdr.name_re);
820 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
821 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
822 else
823 istfree(&rule->expect.hdr.name);
824
825 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
826 regex_free(rule->expect.hdr.value_re);
827 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
828 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
829 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
830 istfree(&rule->expect.hdr.value);
831 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200832 case TCPCHK_EXPECT_CUSTOM:
833 case TCPCHK_EXPECT_UNDEF:
834 break;
835 }
836 break;
837 case TCPCHK_ACT_CONNECT:
838 free(rule->connect.sni);
839 free(rule->connect.alpn);
840 release_sample_expr(rule->connect.port_expr);
841 break;
842 case TCPCHK_ACT_COMMENT:
843 break;
844 case TCPCHK_ACT_ACTION_KW:
845 free(rule->action_kw.rule);
846 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200847 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200848
849 if (in_pool)
850 pool_free(pool_head_tcpcheck_rule, rule);
851 else
852 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200853}
854
Christopher Faulet61cc8522020-04-20 14:54:42 +0200855/* Creates a tcp-check variable used in preset variables before executing a
856 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100857 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200858static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100859{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200860 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100861
Christopher Faulet61cc8522020-04-20 14:54:42 +0200862 var = calloc(1, sizeof(*var));
863 if (var == NULL)
864 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100865
Christopher Fauletb61caf42020-04-21 10:57:42 +0200866 var->name = istdup(name);
867 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200868 free(var);
869 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100870 }
Simon Horman98637e52014-06-20 12:30:16 +0900871
Christopher Faulet61cc8522020-04-20 14:54:42 +0200872 LIST_INIT(&var->list);
873 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900874}
875
Christopher Faulet61cc8522020-04-20 14:54:42 +0200876/* Releases memory allocated for a preset tcp-check variable */
877static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900878{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200879 if (!var)
880 return;
881
Christopher Fauletb61caf42020-04-21 10:57:42 +0200882 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200883 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
884 free(var->data.u.str.area);
885 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
886 free(var->data.u.meth.str.area);
887 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900888}
889
Christopher Faulet61cc8522020-04-20 14:54:42 +0200890/* Releases a list of preset tcp-check variables */
891static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900892{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200893 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200894
Christopher Faulet61cc8522020-04-20 14:54:42 +0200895 list_for_each_entry_safe(var, back, vars, list) {
896 LIST_DEL(&var->list);
897 free_tcpcheck_var(var);
898 }
Simon Horman98637e52014-06-20 12:30:16 +0900899}
900
Christopher Faulet61cc8522020-04-20 14:54:42 +0200901/* Duplicate a list of preset tcp-check variables */
902int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900903{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200904 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900905
Christopher Faulet61cc8522020-04-20 14:54:42 +0200906 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200907 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200908 if (!new)
909 goto error;
910 new->data.type = var->data.type;
911 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
912 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
913 goto error;
914 if (var->data.type == SMP_T_STR)
915 new->data.u.str.area[new->data.u.str.data] = 0;
916 }
917 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
918 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
919 goto error;
920 new->data.u.str.area[new->data.u.str.data] = 0;
921 new->data.u.meth.meth = var->data.u.meth.meth;
922 }
923 else
924 new->data.u = var->data.u;
925 LIST_ADDQ(dst, &new->list);
926 }
927 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900928
Christopher Faulet61cc8522020-04-20 14:54:42 +0200929 error:
930 free(new);
931 return 0;
932}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200933
Christopher Faulet61cc8522020-04-20 14:54:42 +0200934/* Looks for a shared tcp-check ruleset given its name. */
935static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
936{
937 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200938 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900939
Christopher Fauletd7cee712020-04-21 13:45:00 +0200940 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
941 if (node) {
942 rs = container_of(node, typeof(*rs), node);
943 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200944 }
945 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900946}
947
Christopher Faulet56192cc2020-05-29 08:10:50 +0200948/* Creates a new shared tcp-check ruleset and insert it in shared_tcpchecks
949 * tree.
950 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200951static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900952{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200953 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900954
Christopher Faulet61cc8522020-04-20 14:54:42 +0200955 rs = calloc(1, sizeof(*rs));
956 if (rs == NULL)
957 return NULL;
958
Christopher Fauletd7cee712020-04-21 13:45:00 +0200959 rs->node.key = strdup(name);
960 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961 free(rs);
962 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900963 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964
Christopher Faulet61cc8522020-04-20 14:54:42 +0200965 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200966 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200967 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900968}
969
Christopher Faulet61cc8522020-04-20 14:54:42 +0200970/* Releases memory allocated by a tcp-check ruleset. */
971static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900972{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200973 struct tcpcheck_rule *r, *rb;
974 if (!rs)
975 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200976
Christopher Fauletd7cee712020-04-21 13:45:00 +0200977 ebpt_delete(&rs->node);
978 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200979 list_for_each_entry_safe(r, rb, &rs->rules, list) {
980 LIST_DEL(&r->list);
981 free_tcpcheck(r, 0);
982 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200983 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900984}
985
Christopher Faulet61cc8522020-04-20 14:54:42 +0200986
987/**************************************************************************/
988/**************** Everything about tcp-checks execution *******************/
989/**************************************************************************/
990/* Returns the id of a step in a tcp-check ruleset */
991static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200992{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200993 if (!rule)
994 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900995
Christopher Faulet61cc8522020-04-20 14:54:42 +0200996 /* no last started step => first step */
997 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900998 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900999
Christopher Faulet61cc8522020-04-20 14:54:42 +02001000 /* last step is the first implicit connect */
1001 if (rule->index == 0 &&
1002 rule->action == TCPCHK_ACT_CONNECT &&
1003 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1004 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001005
Christopher Faulet61cc8522020-04-20 14:54:42 +02001006 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001007}
1008
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1010 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001011 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001012static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001013{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001014 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001015
Christopher Faulet61cc8522020-04-20 14:54:42 +02001016 list_for_each_entry(r, rules->list, list) {
1017 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1018 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001019 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001020 return NULL;
1021}
Cyril Bontéac92a062014-12-27 22:28:38 +01001022
Christopher Faulet61cc8522020-04-20 14:54:42 +02001023/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1024 * NULL if none was found.
1025 */
1026static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1027{
1028 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001029
Christopher Faulet61cc8522020-04-20 14:54:42 +02001030 list_for_each_entry_rev(r, rules->list, list) {
1031 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1032 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001033 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001034 return NULL;
1035}
Cyril Bontéac92a062014-12-27 22:28:38 +01001036
Christopher Faulet61cc8522020-04-20 14:54:42 +02001037/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1038 * <start> or NULL if non was found. If <start> is NULL, it relies on
1039 * get_first_tcpcheck_rule().
1040 */
1041static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1042{
1043 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001044
Christopher Faulet61cc8522020-04-20 14:54:42 +02001045 if (!start)
1046 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001047
Christopher Faulet61cc8522020-04-20 14:54:42 +02001048 r = LIST_NEXT(&start->list, typeof(r), list);
1049 list_for_each_entry_from(r, rules->list, list) {
1050 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1051 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001052 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001053 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001054}
Simon Horman98637e52014-06-20 12:30:16 +09001055
Simon Horman98637e52014-06-20 12:30:16 +09001056
Christopher Faulet61cc8522020-04-20 14:54:42 +02001057/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1058static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1059 int match, struct ist info)
1060{
1061 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001062
Christopher Faulet61cc8522020-04-20 14:54:42 +02001063 /* Follows these step to produce the info message:
1064 * 1. if info field is already provided, copy it
1065 * 2. if the expect rule provides an onerror log-format string,
1066 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001067 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001068 * 4. Otherwise produce the generic tcp-check info message
1069 */
1070 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001071 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001072 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001073 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001074 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1075 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1076 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001077 }
Simon Horman98637e52014-06-20 12:30:16 +09001078
Christopher Faulet61cc8522020-04-20 14:54:42 +02001079 if (check->type == PR_O2_TCPCHK_CHK &&
1080 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1081 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001082
Christopher Faulet61cc8522020-04-20 14:54:42 +02001083 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1084 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001085 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001086 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1087 break;
1088 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001089 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001090 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001091 tcpcheck_get_step_id(check, rule));
1092 break;
1093 case TCPCHK_EXPECT_BINARY:
1094 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1095 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001096 case TCPCHK_EXPECT_STRING_REGEX:
1097 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1098 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1100 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001101 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001102 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001103 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001104 case TCPCHK_EXPECT_STRING_LF:
1105 case TCPCHK_EXPECT_HTTP_BODY_LF:
1106 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1107 break;
1108 case TCPCHK_EXPECT_BINARY_LF:
1109 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1110 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001111 case TCPCHK_EXPECT_CUSTOM:
1112 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1113 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001114 case TCPCHK_EXPECT_HTTP_HEADER:
1115 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001116 case TCPCHK_EXPECT_UNDEF:
1117 /* Should never happen. */
1118 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001119 }
1120
Christopher Faulet61cc8522020-04-20 14:54:42 +02001121 comment:
1122 /* If the failing expect rule provides a comment, it is concatenated to
1123 * the info message.
1124 */
1125 if (rule->comment) {
1126 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001127 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001128 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001129
Christopher Faulet61cc8522020-04-20 14:54:42 +02001130 /* Finally, the check status code is set if the failing expect rule
1131 * defines a status expression.
1132 */
1133 if (rule->expect.status_expr) {
1134 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001135 rule->expect.status_expr, SMP_T_STR);
1136
1137 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1138 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001140 }
Simon Horman98637e52014-06-20 12:30:16 +09001141
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142 *(b_tail(msg)) = '\0';
1143}
Cyril Bontéac92a062014-12-27 22:28:38 +01001144
Christopher Faulet61cc8522020-04-20 14:54:42 +02001145/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1146static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1147 struct ist info)
1148{
1149 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001150
Christopher Faulet61cc8522020-04-20 14:54:42 +02001151 /* Follows these step to produce the info message:
1152 * 1. if info field is already provided, copy it
1153 * 2. if the expect rule provides an onsucces log-format string,
1154 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001155 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001156 * 4. Otherwise produce the generic tcp-check info message
1157 */
1158 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001159 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001160 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1161 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1162 &rule->expect.onsuccess_fmt);
1163 else if (check->type == PR_O2_TCPCHK_CHK &&
1164 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1165 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001166
Christopher Faulet61cc8522020-04-20 14:54:42 +02001167 /* Finally, the check status code is set if the expect rule defines a
1168 * status expression.
1169 */
1170 if (rule->expect.status_expr) {
1171 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001172 rule->expect.status_expr, SMP_T_STR);
1173
1174 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1175 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001176 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001177 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001178
1179 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001180}
1181
Christopher Faulet61cc8522020-04-20 14:54:42 +02001182/* Builds the server state header used by HTTP health-checks */
1183static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001184{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001185 int sv_state;
1186 int ratio;
1187 char addr[46];
1188 char port[6];
1189 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1190 "UP %d/%d", "UP",
1191 "NOLB %d/%d", "NOLB",
1192 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001193
Christopher Faulet61cc8522020-04-20 14:54:42 +02001194 if (!(s->check.state & CHK_ST_ENABLED))
1195 sv_state = 6;
1196 else if (s->cur_state != SRV_ST_STOPPED) {
1197 if (s->check.health == s->check.rise + s->check.fall - 1)
1198 sv_state = 3; /* UP */
1199 else
1200 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001201
Christopher Faulet61cc8522020-04-20 14:54:42 +02001202 if (s->cur_state == SRV_ST_STOPPING)
1203 sv_state += 2;
1204 } else {
1205 if (s->check.health)
1206 sv_state = 1; /* going up */
1207 else
1208 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001209 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001210
Christopher Faulet61cc8522020-04-20 14:54:42 +02001211 chunk_appendf(buf, srv_hlt_st[sv_state],
1212 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1213 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001214
Christopher Faulet61cc8522020-04-20 14:54:42 +02001215 addr_to_str(&s->addr, addr, sizeof(addr));
1216 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1217 snprintf(port, sizeof(port), "%u", s->svc_port);
1218 else
1219 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001220
Christopher Faulet61cc8522020-04-20 14:54:42 +02001221 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1222 addr, port, s->proxy->id, s->id,
1223 global.node,
1224 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1225 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1226 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1227 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001228
Christopher Faulet61cc8522020-04-20 14:54:42 +02001229 if ((s->cur_state == SRV_ST_STARTING) &&
1230 now.tv_sec < s->last_change + s->slowstart &&
1231 now.tv_sec >= s->last_change) {
1232 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1233 chunk_appendf(buf, "; throttle=%d%%", ratio);
1234 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236 return b_data(buf);
1237}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001238
Christopher Faulet61cc8522020-04-20 14:54:42 +02001239/* Internal functions to parse and validate a MySQL packet in the context of an
1240 * expect rule. It start to parse the input buffer at the offset <offset>. If
1241 * <last_read> is set, no more data are expected.
1242 */
1243static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1244 unsigned int offset, int last_read)
1245{
1246 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1247 enum healthcheck_status status;
1248 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001249 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001251
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001252
Christopher Faulet61cc8522020-04-20 14:54:42 +02001253 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001254 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001255 if (!last_read)
1256 goto wait_more_data;
1257
1258 /* invalid length or truncated response */
1259 status = HCHK_STATUS_L7RSP;
1260 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001261 }
1262
Christopher Faulet61cc8522020-04-20 14:54:42 +02001263 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1264 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1265 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001266
Christopher Faulet61cc8522020-04-20 14:54:42 +02001267 if (b_data(&check->bi) < offset+plen+4) {
1268 if (!last_read)
1269 goto wait_more_data;
1270
1271 /* invalid length or truncated response */
1272 status = HCHK_STATUS_L7RSP;
1273 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001274 }
Simon Horman98637e52014-06-20 12:30:16 +09001275
Christopher Faulet61cc8522020-04-20 14:54:42 +02001276 if (*b_peek(&check->bi, offset+4) == '\xff') {
1277 /* MySQL Error packet always begin with field_count = 0xff */
1278 status = HCHK_STATUS_L7STS;
1279 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1280 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1281 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1282 goto error;
1283 }
Simon Horman98637e52014-06-20 12:30:16 +09001284
Christopher Faulet61cc8522020-04-20 14:54:42 +02001285 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1286 /* Not the last rule, continue */
1287 goto out;
1288 }
Simon Horman98637e52014-06-20 12:30:16 +09001289
Christopher Faulet61cc8522020-04-20 14:54:42 +02001290 /* We set the MySQL Version in description for information purpose
1291 * FIXME : it can be cool to use MySQL Version for other purpose,
1292 * like mark as down old MySQL server.
1293 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001294 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1295 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001296
Christopher Faulet61cc8522020-04-20 14:54:42 +02001297 out:
1298 free_trash_chunk(msg);
1299 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001300
Christopher Faulet61cc8522020-04-20 14:54:42 +02001301 error:
1302 ret = TCPCHK_EVAL_STOP;
1303 check->code = err;
1304 msg = alloc_trash_chunk();
1305 if (msg)
1306 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1307 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1308 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001309
Christopher Faulet61cc8522020-04-20 14:54:42 +02001310 wait_more_data:
1311 ret = TCPCHK_EVAL_WAIT;
1312 goto out;
1313}
Simon Horman98637e52014-06-20 12:30:16 +09001314
Christopher Faulet61cc8522020-04-20 14:54:42 +02001315/* Custom tcp-check expect function to parse and validate the MySQL initial
1316 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1317 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1318 * error occurred.
1319 */
1320static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1321{
1322 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1323}
Simon Horman98637e52014-06-20 12:30:16 +09001324
Christopher Faulet61cc8522020-04-20 14:54:42 +02001325/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1326 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1327 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1328 * an error occurred.
1329 */
1330static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1331{
1332 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001333
Christopher Faulet61cc8522020-04-20 14:54:42 +02001334 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1335 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1336 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001337
Christopher Faulet61cc8522020-04-20 14:54:42 +02001338 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1339}
Simon Horman98637e52014-06-20 12:30:16 +09001340
Christopher Faulet61cc8522020-04-20 14:54:42 +02001341/* Custom tcp-check expect function to parse and validate the LDAP bind response
1342 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1343 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1344 * error occurred.
1345 */
1346static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1347{
1348 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1349 enum healthcheck_status status;
1350 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001351 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001352 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001353
Christopher Faulet61cc8522020-04-20 14:54:42 +02001354 /* Check if the server speaks LDAP (ASN.1/BER)
1355 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1356 * http://tools.ietf.org/html/rfc4511
1357 */
1358 /* size of LDAPMessage */
1359 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001360
Christopher Faulet61cc8522020-04-20 14:54:42 +02001361 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1362 * messageID: 0x02 0x01 0x01: INTEGER 1
1363 * protocolOp: 0x61: bindResponse
1364 */
1365 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1366 status = HCHK_STATUS_L7RSP;
1367 desc = ist("Not LDAPv3 protocol");
1368 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001369 }
Simon Horman98637e52014-06-20 12:30:16 +09001370
Christopher Faulet61cc8522020-04-20 14:54:42 +02001371 /* size of bindResponse */
1372 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001373
Christopher Faulet61cc8522020-04-20 14:54:42 +02001374 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1375 * ldapResult: 0x0a 0x01: ENUMERATION
1376 */
1377 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1378 status = HCHK_STATUS_L7RSP;
1379 desc = ist("Not LDAPv3 protocol");
1380 goto error;
1381 }
Simon Horman98637e52014-06-20 12:30:16 +09001382
Christopher Faulet61cc8522020-04-20 14:54:42 +02001383 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1384 * resultCode
1385 */
1386 check->code = *(b_head(&check->bi) + msglen + 9);
1387 if (check->code) {
1388 status = HCHK_STATUS_L7STS;
1389 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1390 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001391 }
1392
Christopher Faulet1941bab2020-05-05 07:55:50 +02001393 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1394 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001395
Christopher Faulet61cc8522020-04-20 14:54:42 +02001396 out:
1397 free_trash_chunk(msg);
1398 return ret;
1399
1400 error:
1401 ret = TCPCHK_EVAL_STOP;
1402 msg = alloc_trash_chunk();
1403 if (msg)
1404 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1405 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1406 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001407}
1408
Christopher Faulet61cc8522020-04-20 14:54:42 +02001409/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1410 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1411 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001412 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001413static 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 +02001414{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001415 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1416 enum healthcheck_status status;
1417 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001418 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001419 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001420
Willy Tarreaubaaee002006-06-26 02:48:02 +02001421
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 memcpy(&framesz, b_head(&check->bi), 4);
1423 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001424
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425 if (!last_read && b_data(&check->bi) < (4+framesz))
1426 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001427
Christopher Faulet61cc8522020-04-20 14:54:42 +02001428 memset(b_orig(&trash), 0, b_size(&trash));
1429 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1430 status = HCHK_STATUS_L7RSP;
1431 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1432 goto error;
1433 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001434
Christopher Faulet1941bab2020-05-05 07:55:50 +02001435 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1436 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001437
Christopher Faulet61cc8522020-04-20 14:54:42 +02001438 out:
1439 free_trash_chunk(msg);
1440 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001441
Christopher Faulet61cc8522020-04-20 14:54:42 +02001442 error:
1443 ret = TCPCHK_EVAL_STOP;
1444 msg = alloc_trash_chunk();
1445 if (msg)
1446 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1447 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1448 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001449
Christopher Faulet61cc8522020-04-20 14:54:42 +02001450 wait_more_data:
1451 ret = TCPCHK_EVAL_WAIT;
1452 goto out;
1453}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001454
Christopher Faulet61cc8522020-04-20 14:54:42 +02001455/* Custom tcp-check expect function to parse and validate the agent-check
1456 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1457 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1458 */
1459static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1460{
1461 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1462 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1463 const char *hs = NULL; /* health status */
1464 const char *as = NULL; /* admin status */
1465 const char *ps = NULL; /* performance status */
1466 const char *cs = NULL; /* maxconn */
1467 const char *err = NULL; /* first error to report */
1468 const char *wrn = NULL; /* first warning to report */
1469 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001470
Christopher Faulet61cc8522020-04-20 14:54:42 +02001471 /* We're getting an agent check response. The agent could
1472 * have been disabled in the mean time with a long check
1473 * still pending. It is important that we ignore the whole
1474 * response.
1475 */
1476 if (!(check->state & CHK_ST_ENABLED))
1477 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001478
Christopher Faulet61cc8522020-04-20 14:54:42 +02001479 /* The agent supports strings made of a single line ended by the
1480 * first CR ('\r') or LF ('\n'). This line is composed of words
1481 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1482 * line may optionally contained a description of a state change
1483 * after a sharp ('#'), which is only considered if a health state
1484 * is announced.
1485 *
1486 * Words may be composed of :
1487 * - a numeric weight suffixed by the percent character ('%').
1488 * - a health status among "up", "down", "stopped", and "fail".
1489 * - an admin status among "ready", "drain", "maint".
1490 *
1491 * These words may appear in any order. If multiple words of the
1492 * same category appear, the last one wins.
1493 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001494
Christopher Faulet61cc8522020-04-20 14:54:42 +02001495 p = b_head(&check->bi);
1496 while (*p && *p != '\n' && *p != '\r')
1497 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001498
Christopher Faulet61cc8522020-04-20 14:54:42 +02001499 if (!*p) {
1500 if (!last_read)
1501 goto wait_more_data;
1502
1503 /* at least inform the admin that the agent is mis-behaving */
1504 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1505 goto out;
1506 }
1507
1508 *p = 0;
1509 cmd = b_head(&check->bi);
1510
1511 while (*cmd) {
1512 /* look for next word */
1513 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1514 cmd++;
1515 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001516 }
1517
Christopher Faulet61cc8522020-04-20 14:54:42 +02001518 if (*cmd == '#') {
1519 /* this is the beginning of a health status description,
1520 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001521 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001522 cmd++;
1523 while (*cmd == '\t' || *cmd == ' ')
1524 cmd++;
1525 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001526 }
1527
Christopher Faulet61cc8522020-04-20 14:54:42 +02001528 /* find the end of the word so that we have a null-terminated
1529 * word between <cmd> and <p>.
1530 */
1531 p = cmd + 1;
1532 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1533 p++;
1534 if (*p)
1535 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001536
Christopher Faulet61cc8522020-04-20 14:54:42 +02001537 /* first, health statuses */
1538 if (strcasecmp(cmd, "up") == 0) {
1539 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1540 status = HCHK_STATUS_L7OKD;
1541 hs = cmd;
1542 }
1543 else if (strcasecmp(cmd, "down") == 0) {
1544 check->server->check.health = 0;
1545 status = HCHK_STATUS_L7STS;
1546 hs = cmd;
1547 }
1548 else if (strcasecmp(cmd, "stopped") == 0) {
1549 check->server->check.health = 0;
1550 status = HCHK_STATUS_L7STS;
1551 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001552 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001553 else if (strcasecmp(cmd, "fail") == 0) {
1554 check->server->check.health = 0;
1555 status = HCHK_STATUS_L7STS;
1556 hs = cmd;
1557 }
1558 /* admin statuses */
1559 else if (strcasecmp(cmd, "ready") == 0) {
1560 as = cmd;
1561 }
1562 else if (strcasecmp(cmd, "drain") == 0) {
1563 as = cmd;
1564 }
1565 else if (strcasecmp(cmd, "maint") == 0) {
1566 as = cmd;
1567 }
1568 /* try to parse a weight here and keep the last one */
1569 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1570 ps = cmd;
1571 }
1572 /* try to parse a maxconn here */
1573 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1574 cs = cmd;
1575 }
1576 else {
1577 /* keep a copy of the first error */
1578 if (!err)
1579 err = cmd;
1580 }
1581 /* skip to next word */
1582 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001583 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001584 /* here, cmd points either to \0 or to the beginning of a
1585 * description. Skip possible leading spaces.
1586 */
1587 while (*cmd == ' ' || *cmd == '\n')
1588 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001589
Christopher Faulet61cc8522020-04-20 14:54:42 +02001590 /* First, update the admin status so that we avoid sending other
1591 * possibly useless warnings and can also update the health if
1592 * present after going back up.
1593 */
1594 if (as) {
1595 if (strcasecmp(as, "drain") == 0)
1596 srv_adm_set_drain(check->server);
1597 else if (strcasecmp(as, "maint") == 0)
1598 srv_adm_set_maint(check->server);
1599 else
1600 srv_adm_set_ready(check->server);
1601 }
Simon Horman98637e52014-06-20 12:30:16 +09001602
Christopher Faulet61cc8522020-04-20 14:54:42 +02001603 /* now change weights */
1604 if (ps) {
1605 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001606
Christopher Faulet61cc8522020-04-20 14:54:42 +02001607 msg = server_parse_weight_change_request(check->server, ps);
1608 if (!wrn || !*wrn)
1609 wrn = msg;
1610 }
Simon Horman98637e52014-06-20 12:30:16 +09001611
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 if (cs) {
1613 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001614
Christopher Faulet61cc8522020-04-20 14:54:42 +02001615 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001616
Christopher Faulet61cc8522020-04-20 14:54:42 +02001617 msg = server_parse_maxconn_change_request(check->server, cs);
1618 if (!wrn || !*wrn)
1619 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001620 }
1621
Christopher Faulet61cc8522020-04-20 14:54:42 +02001622 /* and finally health status */
1623 if (hs) {
1624 /* We'll report some of the warnings and errors we have
1625 * here. Down reports are critical, we leave them untouched.
1626 * Lack of report, or report of 'UP' leaves the room for
1627 * ERR first, then WARN.
1628 */
1629 const char *msg = cmd;
1630 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001631
Christopher Faulet61cc8522020-04-20 14:54:42 +02001632 if (!*msg || status == HCHK_STATUS_L7OKD) {
1633 if (err && *err)
1634 msg = err;
1635 else if (wrn && *wrn)
1636 msg = wrn;
1637 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001638
Christopher Faulet61cc8522020-04-20 14:54:42 +02001639 t = get_trash_chunk();
1640 chunk_printf(t, "via agent : %s%s%s%s",
1641 hs, *msg ? " (" : "",
1642 msg, *msg ? ")" : "");
1643 set_server_check_status(check, status, t->area);
1644 }
1645 else if (err && *err) {
1646 /* No status change but we'd like to report something odd.
1647 * Just report the current state and copy the message.
1648 */
1649 chunk_printf(&trash, "agent reports an error : %s", err);
1650 set_server_check_status(check, status/*check->status*/, trash.area);
1651 }
1652 else if (wrn && *wrn) {
1653 /* No status change but we'd like to report something odd.
1654 * Just report the current state and copy the message.
1655 */
1656 chunk_printf(&trash, "agent warns : %s", wrn);
1657 set_server_check_status(check, status/*check->status*/, trash.area);
1658 }
1659 else
1660 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001661
Christopher Faulet61cc8522020-04-20 14:54:42 +02001662 out:
1663 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001664
Christopher Faulet61cc8522020-04-20 14:54:42 +02001665 wait_more_data:
1666 ret = TCPCHK_EVAL_WAIT;
1667 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001668}
1669
Christopher Faulet61cc8522020-04-20 14:54:42 +02001670/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1671 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1672 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001673 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001674static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001675{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001676 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1677 struct tcpcheck_connect *connect = &rule->connect;
1678 struct proxy *proxy = check->proxy;
1679 struct server *s = check->server;
1680 struct task *t = check->task;
1681 struct conn_stream *cs;
1682 struct connection *conn = NULL;
1683 struct protocol *proto;
1684 struct xprt_ops *xprt;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001685 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001686 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001687
Christopher Faulet61cc8522020-04-20 14:54:42 +02001688 /* For a connect action we'll create a new connection. We may also have
1689 * to kill a previous one. But we don't want to leave *without* a
1690 * connection if we came here from the connection layer, hence with a
1691 * connection. Thus we'll proceed in the following order :
1692 * 1: close but not release previous connection (handled by the caller)
1693 * 2: try to get a new connection
1694 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001695 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001696
Christopher Faulet61cc8522020-04-20 14:54:42 +02001697 /* 2- prepare new connection */
1698 cs = cs_new(NULL);
1699 if (!cs) {
1700 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1701 tcpcheck_get_step_id(check, rule));
1702 if (rule->comment)
1703 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1704 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1705 ret = TCPCHK_EVAL_STOP;
1706 goto out;
1707 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001708
Christopher Faulet61cc8522020-04-20 14:54:42 +02001709 /* 3- release and replace the old one on success */
1710 if (check->cs) {
1711 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001712 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1713 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001714
1715 /* We may have been scheduled to run, and the I/O handler
1716 * expects to have a cs, so remove the tasklet
1717 */
1718 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1719 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001720 }
1721
Christopher Faulet61cc8522020-04-20 14:54:42 +02001722 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001723
Christopher Faulet61cc8522020-04-20 14:54:42 +02001724 check->cs = cs;
1725 conn = cs->conn;
1726 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001727
Christopher Faulet61cc8522020-04-20 14:54:42 +02001728 /* Maybe there were an older connection we were waiting on */
1729 check->wait_list.events = 0;
1730 conn->target = s ? &s->obj_type : &proxy->obj_type;
1731
1732 /* no client address */
1733 if (!sockaddr_alloc(&conn->dst)) {
1734 status = SF_ERR_RESOURCE;
1735 goto fail_check;
1736 }
1737
1738 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001739 * addr if specified on the server. otherwise, use the server addr (it
1740 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001741 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001742 *conn->dst = (is_addr(&connect->addr)
1743 ? connect->addr
1744 : (is_addr(&check->addr) ? check->addr : s->addr));
1745 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001746
Christopher Faulet61cc8522020-04-20 14:54:42 +02001747 port = 0;
1748 if (!port && connect->port)
1749 port = connect->port;
1750 if (!port && connect->port_expr) {
1751 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001752
Christopher Faulet61cc8522020-04-20 14:54:42 +02001753 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1754 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1755 connect->port_expr, SMP_T_SINT);
1756 if (smp)
1757 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001758 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001759 if (!port && is_inet_addr(&connect->addr))
1760 port = get_host_port(&connect->addr);
1761 if (!port && check->port)
1762 port = check->port;
1763 if (!port && is_inet_addr(&check->addr))
1764 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001765 if (!port) {
1766 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001767 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001768 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001769 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001770
Christopher Faulet61cc8522020-04-20 14:54:42 +02001771 xprt = ((connect->options & TCPCHK_OPT_SSL)
1772 ? xprt_get(XPRT_SSL)
1773 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001774
Christopher Faulet61cc8522020-04-20 14:54:42 +02001775 conn_prepare(conn, proto, xprt);
1776 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001777
Christopher Faulet61cc8522020-04-20 14:54:42 +02001778 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001779 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001780 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001781 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001782
Christopher Faulet61cc8522020-04-20 14:54:42 +02001783 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1784 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001785 if (!next || next->action != TCPCHK_ACT_EXPECT)
1786 flags |= CONNECT_DELACK_ALWAYS;
1787 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001788 }
1789
Christopher Faulet61cc8522020-04-20 14:54:42 +02001790 if (status != SF_ERR_NONE)
1791 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001792
Christopher Faulet61cc8522020-04-20 14:54:42 +02001793 conn->flags |= CO_FL_PRIVATE;
1794 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001795
Christopher Faulet61cc8522020-04-20 14:54:42 +02001796 /* The mux may be initialized now if there isn't server attached to the
1797 * check (email alerts) or if there is a mux proto specified or if there
1798 * is no alpn.
1799 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001800 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1801 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001802 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001803
Christopher Faulet61cc8522020-04-20 14:54:42 +02001804 if (connect->mux_proto)
1805 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001806 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001807 mux_ops = check->mux_proto->mux;
1808 else {
1809 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1810 ? PROTO_MODE_HTTP
1811 : PROTO_MODE_TCP);
1812
1813 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001814 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001815 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1816 status = SF_ERR_INTERNAL;
1817 goto fail_check;
1818 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001819 }
1820
Christopher Faulet61cc8522020-04-20 14:54:42 +02001821#ifdef USE_OPENSSL
1822 if (connect->sni)
1823 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001824 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001825 ssl_sock_set_servername(conn, s->check.sni);
1826
1827 if (connect->alpn)
1828 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001829 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001830 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1831#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001832 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001833 conn->send_proxy_ofs = 1;
1834 conn->flags |= CO_FL_SOCKS4;
1835 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001836 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 +02001837 conn->send_proxy_ofs = 1;
1838 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001839 }
1840
Christopher Faulet61cc8522020-04-20 14:54:42 +02001841 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1842 conn->send_proxy_ofs = 1;
1843 conn->flags |= CO_FL_SEND_PROXY;
1844 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001845 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 +02001846 conn->send_proxy_ofs = 1;
1847 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001848 }
1849
Christopher Faulet61cc8522020-04-20 14:54:42 +02001850 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1851 /* Some servers don't like reset on close */
1852 fdtab[cs->conn->handle.fd].linger_risk = 0;
1853 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001854
Christopher Faulet61cc8522020-04-20 14:54:42 +02001855 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1856 if (xprt_add_hs(conn) < 0)
1857 status = SF_ERR_RESOURCE;
1858 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001859
Christopher Faulet61cc8522020-04-20 14:54:42 +02001860 fail_check:
1861 /* It can return one of :
1862 * - SF_ERR_NONE if everything's OK
1863 * - SF_ERR_SRVTO if there are no more servers
1864 * - SF_ERR_SRVCL if the connection was refused by the server
1865 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1866 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1867 * - SF_ERR_INTERNAL for any other purely internal errors
1868 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1869 * Note that we try to prevent the network stack from sending the ACK during the
1870 * connect() when a pure TCP check is used (without PROXY protocol).
1871 */
1872 switch (status) {
1873 case SF_ERR_NONE:
1874 /* we allow up to min(inter, timeout.connect) for a connection
1875 * to establish but only when timeout.check is set as it may be
1876 * to short for a full check otherwise
1877 */
1878 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001879
Christopher Faulet61cc8522020-04-20 14:54:42 +02001880 if (proxy->timeout.check && proxy->timeout.connect) {
1881 int t_con = tick_add(now_ms, proxy->timeout.connect);
1882 t->expire = tick_first(t->expire, t_con);
1883 }
1884 break;
1885 case SF_ERR_SRVTO: /* ETIMEDOUT */
1886 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1887 case SF_ERR_PRXCOND:
1888 case SF_ERR_RESOURCE:
1889 case SF_ERR_INTERNAL:
1890 chk_report_conn_err(check, errno, 0);
1891 ret = TCPCHK_EVAL_STOP;
1892 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001893 }
1894
Christopher Faulet61cc8522020-04-20 14:54:42 +02001895 /* don't do anything until the connection is established */
1896 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001897 if (conn->mux) {
1898 if (next && next->action == TCPCHK_ACT_SEND)
1899 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1900 else
1901 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1902 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001903 ret = TCPCHK_EVAL_WAIT;
1904 goto out;
1905 }
1906
1907 out:
1908 if (conn && check->result == CHK_RES_FAILED)
1909 conn->flags |= CO_FL_ERROR;
1910 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001911}
1912
Christopher Faulet61cc8522020-04-20 14:54:42 +02001913/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1914 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1915 * TCPCHK_EVAL_STOP if an error occurred.
1916 */
1917static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001918{
1919 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001920 struct tcpcheck_send *send = &rule->send;
1921 struct conn_stream *cs = check->cs;
1922 struct connection *conn = cs_conn(cs);
1923 struct buffer *tmp = NULL;
1924 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001925
Christopher Faulet61cc8522020-04-20 14:54:42 +02001926 /* reset the read & write buffer */
1927 b_reset(&check->bi);
1928 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001929
Christopher Faulet61cc8522020-04-20 14:54:42 +02001930 switch (send->type) {
1931 case TCPCHK_SEND_STRING:
1932 case TCPCHK_SEND_BINARY:
1933 if (istlen(send->data) >= b_size(&check->bo)) {
1934 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1935 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1936 tcpcheck_get_step_id(check, rule));
1937 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1938 ret = TCPCHK_EVAL_STOP;
1939 goto out;
1940 }
1941 b_putist(&check->bo, send->data);
1942 break;
1943 case TCPCHK_SEND_STRING_LF:
1944 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1945 if (!b_data(&check->bo))
1946 goto out;
1947 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001948 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001949 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001950
Christopher Faulet61cc8522020-04-20 14:54:42 +02001951 tmp = alloc_trash_chunk();
1952 if (!tmp)
1953 goto error_lf;
1954 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1955 if (!b_data(tmp))
1956 goto out;
1957 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001958 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001959 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001960 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001961 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001962 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001963 case TCPCHK_SEND_HTTP: {
1964 struct htx_sl *sl;
1965 struct ist meth, uri, vsn, clen, body;
1966 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001967
Christopher Faulet61cc8522020-04-20 14:54:42 +02001968 tmp = alloc_trash_chunk();
1969 if (!tmp)
1970 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001971
Christopher Faulet61cc8522020-04-20 14:54:42 +02001972 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1973 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1974 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001975 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1976 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1977 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1978 }
1979 else
1980 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001981 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001982
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001983 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1984 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001985 slflags |= HTX_SL_F_VER_11;
1986 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1987 if (!isttest(send->http.body))
1988 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001989
Christopher Faulet61cc8522020-04-20 14:54:42 +02001990 htx = htx_from_buf(&check->bo);
1991 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1992 if (!sl)
1993 goto error_htx;
1994 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001995 if (!http_update_host(htx, sl, uri))
1996 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001997
Christopher Faulet61cc8522020-04-20 14:54:42 +02001998 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1999 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02002000 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002001
Christopher Faulet61cc8522020-04-20 14:54:42 +02002002 list_for_each_entry(hdr, &send->http.hdrs, list) {
2003 chunk_reset(tmp);
2004 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2005 if (!b_data(tmp))
2006 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002007 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2008 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002009 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002010 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2011 if (!http_update_authority(htx, sl, hdr_value))
2012 goto error_htx;
2013 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002014 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002015
Christopher Faulet61cc8522020-04-20 14:54:42 +02002016 }
2017 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2018 chunk_reset(tmp);
2019 httpchk_build_status_header(check->server, tmp);
2020 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2021 goto error_htx;
2022 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002023
2024
2025 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2026 chunk_reset(tmp);
2027 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2028 body = ist2(b_orig(tmp), b_data(tmp));
2029 }
2030 else
2031 body = send->http.body;
2032 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2033
2034 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2035 !htx_add_header(htx, ist("Content-length"), clen))
2036 goto error_htx;
2037
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002038
Christopher Faulet61cc8522020-04-20 14:54:42 +02002039 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002040 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002041 !htx_add_endof(htx, HTX_BLK_EOM))
2042 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002043
Christopher Faulet61cc8522020-04-20 14:54:42 +02002044 htx_to_buf(htx, &check->bo);
2045 break;
2046 }
2047 case TCPCHK_SEND_UNDEF:
2048 /* Should never happen. */
2049 ret = TCPCHK_EVAL_STOP;
2050 goto out;
2051 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002052
Christopher Faulet6d471212020-04-22 11:09:25 +02002053
2054 if (conn->mux->snd_buf(cs, &check->bo,
2055 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002056 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002057 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002058 goto out;
2059 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002060 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002061 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002062 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2063 ret = TCPCHK_EVAL_WAIT;
2064 goto out;
2065 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002066
Christopher Faulet61cc8522020-04-20 14:54:42 +02002067 out:
2068 free_trash_chunk(tmp);
2069 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002070
Christopher Faulet61cc8522020-04-20 14:54:42 +02002071 error_htx:
2072 if (htx) {
2073 htx_reset(htx);
2074 htx_to_buf(htx, &check->bo);
2075 }
2076 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2077 tcpcheck_get_step_id(check, rule));
2078 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2079 ret = TCPCHK_EVAL_STOP;
2080 goto out;
2081
2082 error_lf:
2083 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2084 tcpcheck_get_step_id(check, rule));
2085 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2086 ret = TCPCHK_EVAL_STOP;
2087 goto out;
2088
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002089}
2090
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002091/* Try to receive data before evaluating a tcp-check expect rule. Returns
2092 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002093 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2094 * TCPCHK_EVAL_STOP if an error occurred.
2095 */
2096static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002097{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002098 struct conn_stream *cs = check->cs;
2099 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002100 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002101 size_t max, read, cur_read = 0;
2102 int is_empty;
2103 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002104
Christopher Faulet61cc8522020-04-20 14:54:42 +02002105 if (check->wait_list.events & SUB_RETRY_RECV)
2106 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002107
Christopher Faulet61cc8522020-04-20 14:54:42 +02002108 if (cs->flags & CS_FL_EOS)
2109 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002110
Christopher Faulet61cc8522020-04-20 14:54:42 +02002111 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002112
Christopher Faulet61cc8522020-04-20 14:54:42 +02002113 /* prepare to detect if the mux needs more room */
2114 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002115
Christopher Faulet61cc8522020-04-20 14:54:42 +02002116 while ((cs->flags & CS_FL_RCV_MORE) ||
2117 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2118 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2119 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2120 cur_read += read;
2121 if (!read ||
2122 (cs->flags & CS_FL_WANT_ROOM) ||
2123 (--read_poll <= 0) ||
2124 (read < max && read >= global.tune.recv_enough))
2125 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002126 }
2127
Christopher Faulet61cc8522020-04-20 14:54:42 +02002128 end_recv:
2129 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2130 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2131 /* Report network errors only if we got no other data. Otherwise
2132 * we'll let the upper layers decide whether the response is OK
2133 * or not. It is very common that an RST sent by the server is
2134 * reported as an error just after the last data chunk.
2135 */
2136 goto stop;
2137 }
2138 if (!cur_read) {
2139 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2140 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2141 goto wait_more_data;
2142 }
2143 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002144 int status;
2145
Christopher Faulet61cc8522020-04-20 14:54:42 +02002146 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2147 tcpcheck_get_step_id(check, rule));
2148 if (rule->comment)
2149 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002150
2151 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2152 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002153 goto stop;
2154 }
2155 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002156
2157 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002158 return ret;
2159
Christopher Faulet61cc8522020-04-20 14:54:42 +02002160 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002161 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002162 goto out;
2163
2164 wait_more_data:
2165 ret = TCPCHK_EVAL_WAIT;
2166 goto out;
2167}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002168
Christopher Faulet61cc8522020-04-20 14:54:42 +02002169/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2170 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2171 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2172 * error occurred.
2173 */
2174static 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 +02002175{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002176 struct htx *htx = htxbuf(&check->bi);
2177 struct htx_sl *sl;
2178 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002179 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002180 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002181 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002182 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002183 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002184 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002185
Christopher Faulet61cc8522020-04-20 14:54:42 +02002186 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002187
Christopher Faulet61cc8522020-04-20 14:54:42 +02002188 if (htx->flags & HTX_FL_PARSING_ERROR) {
2189 status = HCHK_STATUS_L7RSP;
2190 goto error;
2191 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002192
Christopher Faulet61cc8522020-04-20 14:54:42 +02002193 if (htx_is_empty(htx)) {
2194 if (last_read) {
2195 status = HCHK_STATUS_L7RSP;
2196 goto error;
2197 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002198 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002199 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002200
Christopher Faulet61cc8522020-04-20 14:54:42 +02002201 sl = http_get_stline(htx);
2202 check->code = sl->info.res.status;
2203
2204 if (check->server &&
2205 (check->server->proxy->options & PR_O_DISABLE404) &&
2206 (check->server->next_state != SRV_ST_STOPPED) &&
2207 (check->code == 404)) {
2208 /* 404 may be accepted as "stopping" only if the server was up */
2209 goto out;
2210 }
2211
2212 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2213 /* Make GCC happy ; initialize match to a failure state. */
2214 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002215 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002216
2217 switch (expect->type) {
2218 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002219 match = 0;
2220 for (i = 0; i < expect->codes.num; i++) {
2221 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2222 sl->info.res.status <= expect->codes.codes[i][1]) {
2223 match = 1;
2224 break;
2225 }
2226 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002227
2228 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002229 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002230 if (LIST_ISEMPTY(&expect->onerror_fmt))
2231 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002232 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002233 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002234 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2235
2236 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002237 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002238 if (LIST_ISEMPTY(&expect->onerror_fmt))
2239 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002240 break;
2241
Christopher Faulet39708192020-05-05 10:47:36 +02002242 case TCPCHK_EXPECT_HTTP_HEADER: {
2243 struct http_hdr_ctx ctx;
2244 struct ist npat, vpat, value;
2245 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2246
2247 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2248 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002249 if (!nbuf) {
2250 status = HCHK_STATUS_L7RSP;
2251 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002252 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002253 }
Christopher Faulet39708192020-05-05 10:47:36 +02002254 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 +02002255 if (!b_data(nbuf)) {
2256 status = HCHK_STATUS_L7RSP;
2257 desc = ist("log-format string evaluated to an empty string");
2258 goto error;
2259 }
Christopher Faulet39708192020-05-05 10:47:36 +02002260 npat = ist2(b_orig(nbuf), b_data(nbuf));
2261 }
2262 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2263 npat = expect->hdr.name;
2264
2265 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2266 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002267 if (!vbuf) {
2268 status = HCHK_STATUS_L7RSP;
2269 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002270 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002271 }
Christopher Faulet39708192020-05-05 10:47:36 +02002272 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 +02002273 if (!b_data(vbuf)) {
2274 status = HCHK_STATUS_L7RSP;
2275 desc = ist("log-format string evaluated to an empty string");
2276 goto error;
2277 }
Christopher Faulet39708192020-05-05 10:47:36 +02002278 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2279 }
2280 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2281 vpat = expect->hdr.value;
2282
2283 match = 0;
2284 ctx.blk = NULL;
2285 while (1) {
2286 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2287 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2288 if (!http_find_str_header(htx, npat, &ctx, full))
2289 goto end_of_match;
2290 break;
2291 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2292 if (!http_find_pfx_header(htx, npat, &ctx, full))
2293 goto end_of_match;
2294 break;
2295 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2296 if (!http_find_sfx_header(htx, npat, &ctx, full))
2297 goto end_of_match;
2298 break;
2299 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2300 if (!http_find_sub_header(htx, npat, &ctx, full))
2301 goto end_of_match;
2302 break;
2303 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2304 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2305 goto end_of_match;
2306 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002307 default:
2308 /* should never happen */
2309 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002310 }
2311
Christopher Faulet083eff32020-05-07 15:41:39 +02002312 /* A header has matched the name pattern, let's test its
2313 * value now (always defined from there). If there is no
2314 * value pattern, it is a good match.
2315 */
2316
Christopher Faulet39708192020-05-05 10:47:36 +02002317 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2318 match = 1;
2319 goto end_of_match;
2320 }
2321
2322 value = ctx.value;
2323 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2324 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2325 if (isteq(value, vpat)) {
2326 match = 1;
2327 goto end_of_match;
2328 }
2329 break;
2330 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2331 if (istlen(value) < istlen(vpat))
2332 break;
2333 value = ist2(istptr(value), istlen(vpat));
2334 if (isteq(value, vpat)) {
2335 match = 1;
2336 goto end_of_match;
2337 }
2338 break;
2339 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2340 if (istlen(value) < istlen(vpat))
2341 break;
2342 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2343 if (isteq(value, vpat)) {
2344 match = 1;
2345 goto end_of_match;
2346 }
2347 break;
2348 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2349 if (isttest(istist(value, vpat))) {
2350 match = 1;
2351 goto end_of_match;
2352 }
2353 break;
2354 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2355 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2356 match = 1;
2357 goto end_of_match;
2358 }
2359 break;
2360 }
2361 }
2362
2363 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002364 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002365 if (LIST_ISEMPTY(&expect->onerror_fmt))
2366 desc = htx_sl_res_reason(sl);
2367 break;
2368 }
2369
Christopher Faulet61cc8522020-04-20 14:54:42 +02002370 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002371 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002372 case TCPCHK_EXPECT_HTTP_BODY_LF:
2373 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002374 chunk_reset(&trash);
2375 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2376 enum htx_blk_type type = htx_get_blk_type(blk);
2377
2378 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2379 break;
2380 if (type == HTX_BLK_DATA) {
2381 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2382 break;
2383 }
2384 }
2385
2386 if (!b_data(&trash)) {
2387 if (!last_read)
2388 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002389 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002390 if (LIST_ISEMPTY(&expect->onerror_fmt))
2391 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002392 goto error;
2393 }
2394
Christopher Fauletaaab0832020-05-05 15:54:22 +02002395 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2396 tmp = alloc_trash_chunk();
2397 if (!tmp) {
2398 status = HCHK_STATUS_L7RSP;
2399 desc = ist("Failed to allocate buffer to eval log-format string");
2400 goto error;
2401 }
2402 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2403 if (!b_data(tmp)) {
2404 status = HCHK_STATUS_L7RSP;
2405 desc = ist("log-format string evaluated to an empty string");
2406 goto error;
2407 }
2408 }
2409
Christopher Faulet61cc8522020-04-20 14:54:42 +02002410 if (!last_read &&
2411 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002412 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002413 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2414 ret = TCPCHK_EVAL_WAIT;
2415 goto out;
2416 }
2417
2418 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002419 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002420 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2421 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002422 else
2423 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2424
2425 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002426 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002427 if (LIST_ISEMPTY(&expect->onerror_fmt))
2428 desc = (inverse
2429 ? ist("HTTP check matched unwanted content")
2430 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002431 break;
2432
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002433
Christopher Faulet61cc8522020-04-20 14:54:42 +02002434 default:
2435 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002436 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002437 goto error;
2438 }
2439
Christopher Faulet61cc8522020-04-20 14:54:42 +02002440 /* Wait for more data on mismatch only if no minimum is defined (-1),
2441 * otherwise the absence of match is already conclusive.
2442 */
2443 if (!match && !last_read && (expect->min_recv == -1)) {
2444 ret = TCPCHK_EVAL_WAIT;
2445 goto out;
2446 }
2447
2448 if (!(match ^ inverse))
2449 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002450
2451 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002452 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002453 free_trash_chunk(nbuf);
2454 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002455 free_trash_chunk(msg);
2456 return ret;
2457
2458 error:
2459 ret = TCPCHK_EVAL_STOP;
2460 msg = alloc_trash_chunk();
2461 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002462 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002463 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2464 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002465
2466 wait_more_data:
2467 ret = TCPCHK_EVAL_WAIT;
2468 goto out;
2469}
2470
Christopher Faulet61cc8522020-04-20 14:54:42 +02002471/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2472 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2473 * if an error occurred.
2474 */
2475static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2476{
2477 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2478 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002479 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002480 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002481 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002482 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002483
Christopher Faulet61cc8522020-04-20 14:54:42 +02002484 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002485
Christopher Faulet61cc8522020-04-20 14:54:42 +02002486 /* The current expect might need more data than the previous one, check again
2487 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002488 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002489 if (!last_read) {
2490 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2491 (b_data(&check->bi) < istlen(expect->data))) {
2492 ret = TCPCHK_EVAL_WAIT;
2493 goto out;
2494 }
2495 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2496 ret = TCPCHK_EVAL_WAIT;
2497 goto out;
2498 }
2499 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002500
Christopher Faulet61cc8522020-04-20 14:54:42 +02002501 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2502 /* Make GCC happy ; initialize match to a failure state. */
2503 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002504 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002505
Christopher Faulet61cc8522020-04-20 14:54:42 +02002506 switch (expect->type) {
2507 case TCPCHK_EXPECT_STRING:
2508 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002509 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 +02002510 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002511 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002512 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 +02002513 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002514
Christopher Faulet67a23452020-05-05 18:10:01 +02002515 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002516 chunk_reset(&trash);
2517 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002518 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002519 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002520
2521 case TCPCHK_EXPECT_STRING_LF:
2522 case TCPCHK_EXPECT_BINARY_LF:
2523 match = 0;
2524 tmp = alloc_trash_chunk();
2525 if (!tmp) {
2526 status = HCHK_STATUS_L7RSP;
2527 desc = ist("Failed to allocate buffer to eval format string");
2528 goto error;
2529 }
2530 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2531 if (!b_data(tmp)) {
2532 status = HCHK_STATUS_L7RSP;
2533 desc = ist("log-format string evaluated to an empty string");
2534 goto error;
2535 }
2536 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2537 int len = tmp->data;
2538 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2539 status = HCHK_STATUS_L7RSP;
2540 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2541 goto error;
2542 }
2543 tmp->data = len;
2544 }
2545 if (b_data(&check->bi) < tmp->data) {
2546 if (!last_read) {
2547 ret = TCPCHK_EVAL_WAIT;
2548 goto out;
2549 }
2550 break;
2551 }
2552 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2553 break;
2554
Christopher Faulet61cc8522020-04-20 14:54:42 +02002555 case TCPCHK_EXPECT_CUSTOM:
2556 if (expect->custom)
2557 ret = expect->custom(check, rule, last_read);
2558 goto out;
2559 default:
2560 /* Should never happen. */
2561 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002562 goto out;
2563 }
2564
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002565
Christopher Faulet61cc8522020-04-20 14:54:42 +02002566 /* Wait for more data on mismatch only if no minimum is defined (-1),
2567 * otherwise the absence of match is already conclusive.
2568 */
2569 if (!match && !last_read && (expect->min_recv == -1)) {
2570 ret = TCPCHK_EVAL_WAIT;
2571 goto out;
2572 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002573
Christopher Faulet61cc8522020-04-20 14:54:42 +02002574 /* Result as expected, next rule. */
2575 if (match ^ inverse)
2576 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002577
Christopher Fauletaaab0832020-05-05 15:54:22 +02002578 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002579 /* From this point on, we matched something we did not want, this is an error state. */
2580 ret = TCPCHK_EVAL_STOP;
2581 msg = alloc_trash_chunk();
2582 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002583 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002584 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002585 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002586
Christopher Faulet61cc8522020-04-20 14:54:42 +02002587 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002588 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002589 return ret;
2590}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002591
Christopher Faulet61cc8522020-04-20 14:54:42 +02002592/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002593 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002594 * waits.
2595 */
2596static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2597{
2598 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2599 struct act_rule *act_rule;
2600 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002601
Christopher Faulet61cc8522020-04-20 14:54:42 +02002602 act_rule =rule->action_kw.rule;
2603 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2604 if (act_ret != ACT_RET_CONT) {
2605 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2606 tcpcheck_get_step_id(check, rule));
2607 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2608 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002609 }
2610
Christopher Faulet61cc8522020-04-20 14:54:42 +02002611 return ret;
2612}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002613
Christopher Faulet61cc8522020-04-20 14:54:42 +02002614/* Executes a tcp-check ruleset. Note that this is called both from the
2615 * connection's wake() callback and from the check scheduling task. It returns
2616 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2617 * presenting the risk of an fd replacement.
2618 *
2619 * Please do NOT place any return statement in this function and only leave
2620 * via the out_end_tcpcheck label after setting retcode.
2621 */
2622static int tcpcheck_main(struct check *check)
2623{
2624 struct tcpcheck_rule *rule;
2625 struct conn_stream *cs = check->cs;
2626 struct connection *conn = cs_conn(cs);
2627 int must_read = 1, last_read = 0;
2628 int ret, retcode = 0;
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002629 enum tcpcheck_eval_ret eval_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002630
Christopher Faulet61cc8522020-04-20 14:54:42 +02002631 /* here, we know that the check is complete or that it failed */
2632 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002633 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002634
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002635 /* Note: the conn-stream and the connection may only be undefined before
2636 * the first rule evaluation (it is always a connect rule) or when the
2637 * conn-stream allocation failed on the first connect.
2638 */
2639
Christopher Faulet61cc8522020-04-20 14:54:42 +02002640 /* 1- check for connection error, if any */
2641 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2642 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002643
Christopher Faulet61cc8522020-04-20 14:54:42 +02002644 /* 2- check if we are waiting for the connection establishment. It only
2645 * happens during TCPCHK_ACT_CONNECT. */
2646 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002647 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002648 struct tcpcheck_rule *next;
2649
2650 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2651 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002652 if (!(check->wait_list.events & SUB_RETRY_SEND))
2653 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002654 goto out;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002655 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002656 else {
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002657 eval_ret = tcpcheck_eval_recv(check, check->current_step);
2658 if (eval_ret == TCPCHK_EVAL_STOP)
2659 goto out_end_tcpcheck;
2660 else if (eval_ret == TCPCHK_EVAL_WAIT)
2661 goto out;
2662 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2663 must_read = 0;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002664 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002665 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002666 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002667 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002668
2669 /* 3- check for pending outgoing data. It only happens during
2670 * TCPCHK_ACT_SEND. */
2671 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002672 if (b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002673 /* We're already waiting to be able to send, give up */
2674 if (check->wait_list.events & SUB_RETRY_SEND)
2675 goto out;
2676
Christopher Faulet6d471212020-04-22 11:09:25 +02002677 ret = conn->mux->snd_buf(cs, &check->bo,
2678 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002679 if (ret <= 0) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002680 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002681 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002682 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002683 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002684 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002685 goto out;
2686 }
2687 }
2688 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002689 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002690
Christopher Faulet61cc8522020-04-20 14:54:42 +02002691 /* 4- check if a rule must be resume. It happens if check->current_step
2692 * is defined. */
2693 else if (check->current_step)
2694 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002695
Christopher Faulet61cc8522020-04-20 14:54:42 +02002696 /* 5- It is the first evaluation. We must create a session and preset
2697 * tcp-check variables */
2698 else {
2699 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002700
Christopher Faulet61cc8522020-04-20 14:54:42 +02002701 /* First evaluation, create a session */
2702 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2703 if (!check->sess) {
2704 chunk_printf(&trash, "TCPCHK error allocating check session");
2705 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2706 goto out_end_tcpcheck;
2707 }
2708 vars_init(&check->vars, SCOPE_CHECK);
2709 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002710
Christopher Faulet61cc8522020-04-20 14:54:42 +02002711 /* Preset tcp-check variables */
2712 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2713 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002714
Christopher Faulet61cc8522020-04-20 14:54:42 +02002715 memset(&smp, 0, sizeof(smp));
2716 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2717 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002718 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002719 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002720 }
2721
Christopher Faulet61cc8522020-04-20 14:54:42 +02002722 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002723
Christopher Faulet61cc8522020-04-20 14:54:42 +02002724 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002725 check->code = 0;
2726 switch (rule->action) {
2727 case TCPCHK_ACT_CONNECT:
2728 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002729
Christopher Faulet61cc8522020-04-20 14:54:42 +02002730 /* close but not release yet previous connection */
2731 if (check->cs) {
2732 cs_close(check->cs);
2733 retcode = -1; /* do not reuse the fd in the caller! */
2734 }
2735 eval_ret = tcpcheck_eval_connect(check, rule);
Christopher Faulet3cbdd222020-05-26 11:14:50 +02002736
2737 /* Refresh conn-stream and connection */
2738 cs = check->cs;
2739 conn = cs_conn(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002740 must_read = 1; last_read = 0;
2741 break;
2742 case TCPCHK_ACT_SEND:
2743 check->current_step = rule;
2744 eval_ret = tcpcheck_eval_send(check, rule);
2745 must_read = 1;
2746 break;
2747 case TCPCHK_ACT_EXPECT:
2748 check->current_step = rule;
2749 if (must_read) {
2750 if (check->proxy->timeout.check)
2751 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002752
Christopher Faulet61cc8522020-04-20 14:54:42 +02002753 eval_ret = tcpcheck_eval_recv(check, rule);
2754 if (eval_ret == TCPCHK_EVAL_STOP)
2755 goto out_end_tcpcheck;
2756 else if (eval_ret == TCPCHK_EVAL_WAIT)
2757 goto out;
2758 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2759 must_read = 0;
2760 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002761
Christopher Faulet61cc8522020-04-20 14:54:42 +02002762 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2763 ? tcpcheck_eval_expect_http(check, rule, last_read)
2764 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002765
Christopher Faulet61cc8522020-04-20 14:54:42 +02002766 if (eval_ret == TCPCHK_EVAL_WAIT) {
2767 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002768 if (!(check->wait_list.events & SUB_RETRY_RECV))
2769 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002770 }
2771 break;
2772 case TCPCHK_ACT_ACTION_KW:
2773 /* Don't update the current step */
2774 eval_ret = tcpcheck_eval_action_kw(check, rule);
2775 break;
2776 default:
2777 /* Otherwise, just go to the next one and don't update
2778 * the current step
2779 */
2780 eval_ret = TCPCHK_EVAL_CONTINUE;
2781 break;
2782 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002783
Christopher Faulet61cc8522020-04-20 14:54:42 +02002784 switch (eval_ret) {
2785 case TCPCHK_EVAL_CONTINUE:
2786 break;
2787 case TCPCHK_EVAL_WAIT:
2788 goto out;
2789 case TCPCHK_EVAL_STOP:
2790 goto out_end_tcpcheck;
2791 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002792 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002793
Christopher Faulet61cc8522020-04-20 14:54:42 +02002794 /* All rules was evaluated */
2795 if (check->current_step) {
2796 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002797
Christopher Faulet61cc8522020-04-20 14:54:42 +02002798 if (rule->action == TCPCHK_ACT_EXPECT) {
2799 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002800 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002801
Christopher Faulet61cc8522020-04-20 14:54:42 +02002802 if (check->server &&
2803 (check->server->proxy->options & PR_O_DISABLE404) &&
2804 (check->server->next_state != SRV_ST_STOPPED) &&
2805 (check->code == 404)) {
2806 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2807 goto out_end_tcpcheck;
2808 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002809
Christopher Faulet61cc8522020-04-20 14:54:42 +02002810 msg = alloc_trash_chunk();
2811 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002812 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002813 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2814 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002815 free_trash_chunk(msg);
2816 }
2817 else if (rule->action == TCPCHK_ACT_CONNECT) {
2818 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002819 enum healthcheck_status status = HCHK_STATUS_L4OK;
2820#ifdef USE_OPENSSL
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002821 if (ssl_sock_is_ssl(conn))
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002822 status = HCHK_STATUS_L6OK;
2823#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002824 set_server_check_status(check, status, msg);
2825 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002826 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002827 else
2828 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002829
Christopher Faulet61cc8522020-04-20 14:54:42 +02002830 out_end_tcpcheck:
2831 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2832 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002833
Christopher Faulet61cc8522020-04-20 14:54:42 +02002834 out:
2835 return retcode;
2836}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002837
Christopher Faulet14cd3162020-04-16 14:50:06 +02002838
Christopher Faulet61cc8522020-04-20 14:54:42 +02002839/**************************************************************************/
2840/************** Health-checks based on an external process ****************/
2841/**************************************************************************/
2842static struct list pid_list = LIST_HEAD_INIT(pid_list);
2843static struct pool_head *pool_head_pid_list;
2844__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002845
Christopher Faulet61cc8522020-04-20 14:54:42 +02002846struct extcheck_env {
2847 char *name; /* environment variable name */
2848 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2849};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002850
Christopher Faulet61cc8522020-04-20 14:54:42 +02002851/* environment variables memory requirement for different types of data */
2852#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2853 * such environment variables are not updatable. */
2854#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2855#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2856#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002857
Christopher Faulet61cc8522020-04-20 14:54:42 +02002858/* external checks environment variables */
2859enum {
2860 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002861
Christopher Faulet61cc8522020-04-20 14:54:42 +02002862 /* Proxy specific environment variables */
2863 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2864 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2865 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2866 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002867
Christopher Faulet61cc8522020-04-20 14:54:42 +02002868 /* Server specific environment variables */
2869 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2870 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2871 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2872 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2873 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2874 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002875
Christopher Faulet61cc8522020-04-20 14:54:42 +02002876 EXTCHK_SIZE
2877};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002878
Christopher Faulet61cc8522020-04-20 14:54:42 +02002879const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2880 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2881 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2882 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2883 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2884 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2885 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2886 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2887 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2888 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2889 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2890 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2891};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002892
Christopher Faulet61cc8522020-04-20 14:54:42 +02002893void block_sigchld(void)
2894{
2895 sigset_t set;
2896 sigemptyset(&set);
2897 sigaddset(&set, SIGCHLD);
2898 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2899}
Willy Tarreaube373152018-09-06 11:45:30 +02002900
Christopher Faulet61cc8522020-04-20 14:54:42 +02002901void unblock_sigchld(void)
2902{
2903 sigset_t set;
2904 sigemptyset(&set);
2905 sigaddset(&set, SIGCHLD);
2906 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002907}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002908
Christopher Faulet61cc8522020-04-20 14:54:42 +02002909static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002910{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002911 struct pid_list *elem;
2912 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002913
Christopher Faulet61cc8522020-04-20 14:54:42 +02002914 elem = pool_alloc(pool_head_pid_list);
2915 if (!elem)
2916 return NULL;
2917 elem->pid = pid;
2918 elem->t = t;
2919 elem->exited = 0;
2920 check->curpid = elem;
2921 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002922
Christopher Faulet61cc8522020-04-20 14:54:42 +02002923 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2924 LIST_ADD(&pid_list, &elem->list);
2925 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002926
Christopher Faulet61cc8522020-04-20 14:54:42 +02002927 return elem;
2928}
Christopher Faulete5870d82020-04-15 11:32:03 +02002929
Christopher Faulet61cc8522020-04-20 14:54:42 +02002930static void pid_list_del(struct pid_list *elem)
2931{
2932 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002933
Christopher Faulet61cc8522020-04-20 14:54:42 +02002934 if (!elem)
2935 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002936
Christopher Faulet61cc8522020-04-20 14:54:42 +02002937 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2938 LIST_DEL(&elem->list);
2939 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002940
Christopher Faulet61cc8522020-04-20 14:54:42 +02002941 if (!elem->exited)
2942 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002943
Christopher Faulet61cc8522020-04-20 14:54:42 +02002944 check = elem->t->context;
2945 check->curpid = NULL;
2946 pool_free(pool_head_pid_list, elem);
2947}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002948
Christopher Faulet61cc8522020-04-20 14:54:42 +02002949/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2950static void pid_list_expire(pid_t pid, int status)
2951{
2952 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002953
Christopher Faulet61cc8522020-04-20 14:54:42 +02002954 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2955 list_for_each_entry(elem, &pid_list, list) {
2956 if (elem->pid == pid) {
2957 elem->t->expire = now_ms;
2958 elem->status = status;
2959 elem->exited = 1;
2960 task_wakeup(elem->t, TASK_WOKEN_IO);
2961 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002962 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002963 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002964 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2965}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002966
Christopher Faulet61cc8522020-04-20 14:54:42 +02002967static void sigchld_handler(struct sig_handler *sh)
2968{
2969 pid_t pid;
2970 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002971
Christopher Faulet61cc8522020-04-20 14:54:42 +02002972 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2973 pid_list_expire(pid, status);
2974}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002975
Christopher Faulet61cc8522020-04-20 14:54:42 +02002976static int init_pid_list(void)
2977{
2978 if (pool_head_pid_list != NULL)
2979 /* Nothing to do */
2980 return 0;
2981
2982 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2983 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2984 strerror(errno));
2985 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002986 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002987
Christopher Faulet61cc8522020-04-20 14:54:42 +02002988 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2989 if (pool_head_pid_list == NULL) {
2990 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2991 strerror(errno));
2992 return 1;
2993 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002994
Christopher Faulet61cc8522020-04-20 14:54:42 +02002995 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002996}
2997
Christopher Faulet61cc8522020-04-20 14:54:42 +02002998/* helper macro to set an environment variable and jump to a specific label on failure. */
2999#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003000
Christopher Faulet61cc8522020-04-20 14:54:42 +02003001/*
3002 * helper function to allocate enough memory to store an environment variable.
3003 * It will also check that the environment variable is updatable, and silently
3004 * fail if not.
3005 */
3006static int extchk_setenv(struct check *check, int idx, const char *value)
3007{
3008 int len, ret;
3009 char *envname;
3010 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003011
Christopher Faulet61cc8522020-04-20 14:54:42 +02003012 if (idx < 0 || idx >= EXTCHK_SIZE) {
3013 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
3014 return 1;
3015 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003016
Christopher Faulet61cc8522020-04-20 14:54:42 +02003017 envname = extcheck_envs[idx].name;
3018 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003019
Christopher Faulet61cc8522020-04-20 14:54:42 +02003020 /* Check if the environment variable is already set, and silently reject
3021 * the update if this one is not updatable. */
3022 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3023 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003024
Christopher Faulet61cc8522020-04-20 14:54:42 +02003025 /* Instead of sending NOT_USED, sending an empty value is preferable */
3026 if (strcmp(value, "NOT_USED") == 0) {
3027 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003028 }
3029
Christopher Faulet61cc8522020-04-20 14:54:42 +02003030 len = strlen(envname) + 1;
3031 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3032 len += strlen(value);
3033 else
3034 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003035
Christopher Faulet61cc8522020-04-20 14:54:42 +02003036 if (!check->envp[idx])
3037 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003038
Christopher Faulet61cc8522020-04-20 14:54:42 +02003039 if (!check->envp[idx]) {
3040 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3041 return 1;
3042 }
3043 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3044 if (ret < 0) {
3045 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3046 return 1;
3047 }
3048 else if (ret > len) {
3049 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3050 return 1;
3051 }
3052 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003053}
3054
Christopher Faulet61cc8522020-04-20 14:54:42 +02003055static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003056{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003057 struct server *s = check->server;
3058 struct proxy *px = s->proxy;
3059 struct listener *listener = NULL, *l;
3060 int i;
3061 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3062 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003063
Christopher Faulet61cc8522020-04-20 14:54:42 +02003064 list_for_each_entry(l, &px->conf.listeners, by_fe)
3065 /* Use the first INET, INET6 or UNIX listener */
3066 if (l->addr.ss_family == AF_INET ||
3067 l->addr.ss_family == AF_INET6 ||
3068 l->addr.ss_family == AF_UNIX) {
3069 listener = l;
3070 break;
3071 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003072
Christopher Faulet61cc8522020-04-20 14:54:42 +02003073 check->curpid = NULL;
3074 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3075 if (!check->envp) {
3076 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3077 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003078 }
3079
Christopher Faulet61cc8522020-04-20 14:54:42 +02003080 check->argv = calloc(6, sizeof(char *));
3081 if (!check->argv) {
3082 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3083 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003084 }
3085
Christopher Faulet61cc8522020-04-20 14:54:42 +02003086 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003087
Christopher Faulet61cc8522020-04-20 14:54:42 +02003088 if (!listener) {
3089 check->argv[1] = strdup("NOT_USED");
3090 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003091 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003092 else if (listener->addr.ss_family == AF_INET ||
3093 listener->addr.ss_family == AF_INET6) {
3094 addr_to_str(&listener->addr, buf, sizeof(buf));
3095 check->argv[1] = strdup(buf);
3096 port_to_str(&listener->addr, buf, sizeof(buf));
3097 check->argv[2] = strdup(buf);
3098 }
3099 else if (listener->addr.ss_family == AF_UNIX) {
3100 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003101
Christopher Faulet61cc8522020-04-20 14:54:42 +02003102 un = (struct sockaddr_un *)&listener->addr;
3103 check->argv[1] = strdup(un->sun_path);
3104 check->argv[2] = strdup("NOT_USED");
3105 }
3106 else {
3107 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3108 goto err;
3109 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003110
Christopher Faulet61cc8522020-04-20 14:54:42 +02003111 if (!check->argv[1] || !check->argv[2]) {
3112 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3113 goto err;
3114 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003115
Christopher Faulet61cc8522020-04-20 14:54:42 +02003116 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3117 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3118 if (!check->argv[3] || !check->argv[4]) {
3119 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3120 goto err;
3121 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003122
Christopher Faulet61cc8522020-04-20 14:54:42 +02003123 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3124 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3125 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003126
Christopher Faulet61cc8522020-04-20 14:54:42 +02003127 for (i = 0; i < 5; i++) {
3128 if (!check->argv[i]) {
3129 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3130 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003131 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003132 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003133
Christopher Faulet61cc8522020-04-20 14:54:42 +02003134 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3135 /* Add proxy environment variables */
3136 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3137 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3138 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3139 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3140 /* Add server environment variables */
3141 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3142 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3143 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3144 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3145 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3146 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003147
Christopher Faulet61cc8522020-04-20 14:54:42 +02003148 /* Ensure that we don't leave any hole in check->envp */
3149 for (i = 0; i < EXTCHK_SIZE; i++)
3150 if (!check->envp[i])
3151 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003152
Christopher Faulet61cc8522020-04-20 14:54:42 +02003153 return 1;
3154err:
3155 if (check->envp) {
3156 for (i = 0; i < EXTCHK_SIZE; i++)
3157 free(check->envp[i]);
3158 free(check->envp);
3159 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003160 }
3161
Christopher Faulet61cc8522020-04-20 14:54:42 +02003162 if (check->argv) {
3163 for (i = 1; i < 5; i++)
3164 free(check->argv[i]);
3165 free(check->argv);
3166 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003167 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003168 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003169}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003170
Christopher Faulet61cc8522020-04-20 14:54:42 +02003171/*
3172 * establish a server health-check that makes use of a process.
3173 *
3174 * It can return one of :
3175 * - SF_ERR_NONE if everything's OK
3176 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3177 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3178 *
3179 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003180 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003181static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003182{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003183 char buf[256];
3184 struct check *check = t->context;
3185 struct server *s = check->server;
3186 struct proxy *px = s->proxy;
3187 int status;
3188 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003189
Christopher Faulet61cc8522020-04-20 14:54:42 +02003190 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003191
Christopher Faulet61cc8522020-04-20 14:54:42 +02003192 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003193
Christopher Faulet61cc8522020-04-20 14:54:42 +02003194 pid = fork();
3195 if (pid < 0) {
3196 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3197 (global.tune.options & GTUNE_INSECURE_FORK) ?
3198 "" : " (likely caused by missing 'insecure-fork-wanted')",
3199 strerror(errno));
3200 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003201 goto out;
3202 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003203 if (pid == 0) {
3204 /* Child */
3205 extern char **environ;
3206 struct rlimit limit;
3207 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003208
Christopher Faulet61cc8522020-04-20 14:54:42 +02003209 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3210 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003211
Christopher Faulet61cc8522020-04-20 14:54:42 +02003212 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003213
Christopher Faulet61cc8522020-04-20 14:54:42 +02003214 /* restore the initial FD limits */
3215 limit.rlim_cur = rlim_fd_cur_at_boot;
3216 limit.rlim_max = rlim_fd_max_at_boot;
3217 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3218 getrlimit(RLIMIT_NOFILE, &limit);
3219 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3220 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3221 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3222 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003223
Christopher Faulet61cc8522020-04-20 14:54:42 +02003224 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003225
Christopher Faulet61cc8522020-04-20 14:54:42 +02003226 /* Update some environment variables and command args: curconn, server addr and server port */
3227 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003228
Christopher Faulet61cc8522020-04-20 14:54:42 +02003229 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3230 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003231
Christopher Faulet61cc8522020-04-20 14:54:42 +02003232 *check->argv[4] = 0;
3233 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3234 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3235 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003236
Christopher Faulet61cc8522020-04-20 14:54:42 +02003237 haproxy_unblock_signals();
3238 execvp(px->check_command, check->argv);
3239 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3240 strerror(errno));
3241 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003242 }
3243
Christopher Faulet61cc8522020-04-20 14:54:42 +02003244 /* Parent */
3245 if (check->result == CHK_RES_UNKNOWN) {
3246 if (pid_list_add(pid, t) != NULL) {
3247 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3248
3249 if (px->timeout.check && px->timeout.connect) {
3250 int t_con = tick_add(now_ms, px->timeout.connect);
3251 t->expire = tick_first(t->expire, t_con);
3252 }
3253 status = SF_ERR_NONE;
3254 goto out;
3255 }
3256 else {
3257 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3258 }
3259 kill(pid, SIGTERM); /* process creation error */
3260 }
3261 else
3262 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3263
3264out:
3265 unblock_sigchld();
3266 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003267}
3268
Christopher Faulet61cc8522020-04-20 14:54:42 +02003269/*
3270 * manages a server health-check that uses an external process. Returns
3271 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003272 *
3273 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003274 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003275 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003276static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003277{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003278 struct check *check = context;
3279 struct server *s = check->server;
3280 int rv;
3281 int ret;
3282 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003283
Christopher Faulet61cc8522020-04-20 14:54:42 +02003284 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3285 if (!(check->state & CHK_ST_INPROGRESS)) {
3286 /* no check currently running */
3287 if (!expired) /* woke up too early */
3288 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003289
Christopher Faulet61cc8522020-04-20 14:54:42 +02003290 /* we don't send any health-checks when the proxy is
3291 * stopped, the server should not be checked or the check
3292 * is disabled.
3293 */
3294 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3295 s->proxy->state == PR_STSTOPPED)
3296 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003297
Christopher Faulet61cc8522020-04-20 14:54:42 +02003298 /* we'll initiate a new check */
3299 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003300
Christopher Faulet61cc8522020-04-20 14:54:42 +02003301 check->state |= CHK_ST_INPROGRESS;
3302
3303 ret = connect_proc_chk(t);
3304 if (ret == SF_ERR_NONE) {
3305 /* the process was forked, we allow up to min(inter,
3306 * timeout.connect) for it to report its status, but
3307 * only when timeout.check is set as it may be to short
3308 * for a full check otherwise.
3309 */
3310 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3311
3312 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3313 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3314 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003315 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003316 task_set_affinity(t, tid_bit);
3317 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003318 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003319
Christopher Faulet61cc8522020-04-20 14:54:42 +02003320 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003321
Christopher Faulet61cc8522020-04-20 14:54:42 +02003322 check->state &= ~CHK_ST_INPROGRESS;
3323 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003324
Christopher Faulet61cc8522020-04-20 14:54:42 +02003325 /* we allow up to min(inter, timeout.connect) for a connection
3326 * to establish but only when timeout.check is set
3327 * as it may be to short for a full check otherwise
3328 */
3329 while (tick_is_expired(t->expire, now_ms)) {
3330 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003331
Christopher Faulet61cc8522020-04-20 14:54:42 +02003332 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3333 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003334
Christopher Faulet61cc8522020-04-20 14:54:42 +02003335 if (s->proxy->timeout.check)
3336 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003337 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003338 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003339 else {
3340 /* there was a test running.
3341 * First, let's check whether there was an uncaught error,
3342 * which can happen on connect timeout or error.
3343 */
3344 if (check->result == CHK_RES_UNKNOWN) {
3345 /* good connection is enough for pure TCP check */
3346 struct pid_list *elem = check->curpid;
3347 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003348
Christopher Faulet61cc8522020-04-20 14:54:42 +02003349 if (elem->exited) {
3350 status = elem->status; /* Save in case the process exits between use below */
3351 if (!WIFEXITED(status))
3352 check->code = -1;
3353 else
3354 check->code = WEXITSTATUS(status);
3355 if (!WIFEXITED(status) || WEXITSTATUS(status))
3356 status = HCHK_STATUS_PROCERR;
3357 else
3358 status = HCHK_STATUS_PROCOK;
3359 } else if (expired) {
3360 status = HCHK_STATUS_PROCTOUT;
3361 ha_warning("kill %d\n", (int)elem->pid);
3362 kill(elem->pid, SIGTERM);
3363 }
3364 set_server_check_status(check, status, NULL);
3365 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003366
Christopher Faulet61cc8522020-04-20 14:54:42 +02003367 if (check->result == CHK_RES_FAILED) {
3368 /* a failure or timeout detected */
3369 check_notify_failure(check);
3370 }
3371 else if (check->result == CHK_RES_CONDPASS) {
3372 /* check is OK but asks for stopping mode */
3373 check_notify_stopping(check);
3374 }
3375 else if (check->result == CHK_RES_PASSED) {
3376 /* a success was detected */
3377 check_notify_success(check);
3378 }
3379 task_set_affinity(t, 1);
3380 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003381
Christopher Faulet61cc8522020-04-20 14:54:42 +02003382 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003383
Christopher Faulet61cc8522020-04-20 14:54:42 +02003384 rv = 0;
3385 if (global.spread_checks > 0) {
3386 rv = srv_getinter(check) * global.spread_checks / 100;
3387 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3388 }
3389 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3390 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003391
Christopher Faulet61cc8522020-04-20 14:54:42 +02003392 reschedule:
3393 while (tick_is_expired(t->expire, now_ms))
3394 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003395
Christopher Faulet61cc8522020-04-20 14:54:42 +02003396 out_unlock:
3397 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3398 return t;
3399}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003400
Baptiste Assmann248f1172018-03-01 21:49:01 +01003401
Christopher Faulet61cc8522020-04-20 14:54:42 +02003402/**************************************************************************/
3403/***************** Health-checks based on connections *********************/
3404/**************************************************************************/
3405/* This function is used only for server health-checks. It handles connection
3406 * status updates including errors. If necessary, it wakes the check task up.
3407 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3408 * connection (eg: reconnect). It relies on tcpcheck_main().
3409 */
3410static int wake_srv_chk(struct conn_stream *cs)
3411{
3412 struct connection *conn = cs->conn;
3413 struct check *check = cs->data;
3414 struct email_alertq *q = container_of(check, typeof(*q), check);
3415 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003416
Christopher Faulet61cc8522020-04-20 14:54:42 +02003417 if (check->server)
3418 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3419 else
3420 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003421
Christopher Faulet61cc8522020-04-20 14:54:42 +02003422 /* we may have to make progress on the TCP checks */
3423 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003424
Christopher Faulet61cc8522020-04-20 14:54:42 +02003425 cs = check->cs;
3426 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003427
Christopher Faulet61cc8522020-04-20 14:54:42 +02003428 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3429 /* We may get error reports bypassing the I/O handlers, typically
3430 * the case when sending a pure TCP check which fails, then the I/O
3431 * handlers above are not called. This is completely handled by the
3432 * main processing task so let's simply wake it up. If we get here,
3433 * we expect errno to still be valid.
3434 */
3435 chk_report_conn_err(check, errno, 0);
3436 task_wakeup(check->task, TASK_WOKEN_IO);
3437 }
3438
3439 if (check->result != CHK_RES_UNKNOWN) {
3440 /* Check complete or aborted. If connection not yet closed do it
3441 * now and wake the check task up to be sure the result is
3442 * handled ASAP. */
3443 conn_sock_drain(conn);
3444 cs_close(cs);
3445 ret = -1;
3446 /* We may have been scheduled to run, and the
3447 * I/O handler expects to have a cs, so remove
3448 * the tasklet
3449 */
3450 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3451 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003452 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003453
3454 if (check->server)
3455 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003456 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003457 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003458
Christopher Faulet61cc8522020-04-20 14:54:42 +02003459 /* if a connection got replaced, we must absolutely prevent the connection
3460 * handler from touching its fd, and perform the FD polling updates ourselves
3461 */
3462 if (ret < 0)
3463 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003464
Christopher Faulet61cc8522020-04-20 14:54:42 +02003465 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003466}
3467
Christopher Faulet61cc8522020-04-20 14:54:42 +02003468/* This function checks if any I/O is wanted, and if so, attempts to do so */
3469static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003470{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003471 struct check *check = ctx;
3472 struct conn_stream *cs = check->cs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003473
Christopher Faulet3d5e1212020-05-28 14:34:02 +02003474 wake_srv_chk(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003475 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003476}
3477
Christopher Faulet61cc8522020-04-20 14:54:42 +02003478/* manages a server health-check that uses a connection. Returns
3479 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3480 *
3481 * Please do NOT place any return statement in this function and only leave
3482 * via the out_unlock label.
3483 */
3484static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003485{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003486 struct check *check = context;
3487 struct proxy *proxy = check->proxy;
3488 struct conn_stream *cs = check->cs;
3489 struct connection *conn = cs_conn(cs);
3490 int rv;
3491 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003492
Christopher Faulet61cc8522020-04-20 14:54:42 +02003493 if (check->server)
3494 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3495 if (!(check->state & CHK_ST_INPROGRESS)) {
3496 /* no check currently running */
3497 if (!expired) /* woke up too early */
3498 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003499
Christopher Faulet61cc8522020-04-20 14:54:42 +02003500 /* we don't send any health-checks when the proxy is
3501 * stopped, the server should not be checked or the check
3502 * is disabled.
3503 */
3504 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3505 proxy->state == PR_STSTOPPED)
3506 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003507
Christopher Faulet61cc8522020-04-20 14:54:42 +02003508 /* we'll initiate a new check */
3509 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003510
Christopher Faulet61cc8522020-04-20 14:54:42 +02003511 check->state |= CHK_ST_INPROGRESS;
3512 b_reset(&check->bi);
3513 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003514
Christopher Faulet61cc8522020-04-20 14:54:42 +02003515 task_set_affinity(t, tid_bit);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003516
Christopher Faulet99ff1052020-05-25 07:32:01 +02003517 check->current_step = NULL;
3518 tcpcheck_main(check);
3519 goto out_unlock;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003520 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003521 else {
3522 /* there was a test running.
3523 * First, let's check whether there was an uncaught error,
3524 * which can happen on connect timeout or error.
3525 */
3526 if (check->result == CHK_RES_UNKNOWN) {
3527 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3528 chk_report_conn_err(check, 0, expired);
3529 }
3530 else
3531 goto out_unlock; /* timeout not reached, wait again */
3532 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003533
Christopher Faulet61cc8522020-04-20 14:54:42 +02003534 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003535
Christopher Faulet61cc8522020-04-20 14:54:42 +02003536 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003537
Christopher Faulet61cc8522020-04-20 14:54:42 +02003538 if (conn && conn->xprt) {
3539 /* The check was aborted and the connection was not yet closed.
3540 * This can happen upon timeout, or when an external event such
3541 * as a failed response coupled with "observe layer7" caused the
3542 * server state to be suddenly changed.
3543 */
3544 conn_sock_drain(conn);
3545 cs_close(cs);
3546 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003547
Christopher Faulet61cc8522020-04-20 14:54:42 +02003548 if (cs) {
3549 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003550 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003551 /* We may have been scheduled to run, and the
3552 * I/O handler expects to have a cs, so remove
3553 * the tasklet
3554 */
3555 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3556 cs_destroy(cs);
3557 cs = check->cs = NULL;
3558 conn = NULL;
3559 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003560
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003561 if (check->sess != NULL) {
3562 vars_prune(&check->vars, check->sess, NULL);
3563 session_free(check->sess);
3564 check->sess = NULL;
3565 }
3566
Christopher Faulet61cc8522020-04-20 14:54:42 +02003567 if (check->server) {
3568 if (check->result == CHK_RES_FAILED) {
3569 /* a failure or timeout detected */
3570 check_notify_failure(check);
3571 }
3572 else if (check->result == CHK_RES_CONDPASS) {
3573 /* check is OK but asks for stopping mode */
3574 check_notify_stopping(check);
3575 }
3576 else if (check->result == CHK_RES_PASSED) {
3577 /* a success was detected */
3578 check_notify_success(check);
3579 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003580 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003581 task_set_affinity(t, MAX_THREADS_MASK);
3582 check->state &= ~CHK_ST_INPROGRESS;
3583
3584 if (check->server) {
3585 rv = 0;
3586 if (global.spread_checks > 0) {
3587 rv = srv_getinter(check) * global.spread_checks / 100;
3588 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3589 }
3590 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003591 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003592 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003593
Christopher Faulet61cc8522020-04-20 14:54:42 +02003594 reschedule:
3595 while (tick_is_expired(t->expire, now_ms))
3596 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3597 out_unlock:
3598 if (check->server)
3599 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3600 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003601}
3602
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003603
Christopher Faulet61cc8522020-04-20 14:54:42 +02003604/**************************************************************************/
3605/******************* Internals to parse tcp-check rules *******************/
3606/**************************************************************************/
3607struct action_kw_list tcp_check_keywords = {
3608 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3609};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003610
Christopher Faulet61cc8522020-04-20 14:54:42 +02003611/* Return the struct action_kw associated to a keyword */
3612static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003613{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003614 return action_lookup(&tcp_check_keywords.list, kw);
3615}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003616
Christopher Faulet61cc8522020-04-20 14:54:42 +02003617static void action_kw_tcp_check_build_list(struct buffer *chk)
3618{
3619 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003620}
3621
Christopher Faulet61cc8522020-04-20 14:54:42 +02003622/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3623 * returned on error.
3624 */
3625static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3626 struct list *rules, struct action_kw *kw,
3627 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003628{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003629 struct tcpcheck_rule *chk = NULL;
3630 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003631
Christopher Faulet61cc8522020-04-20 14:54:42 +02003632 actrule = calloc(1, sizeof(*actrule));
3633 if (!actrule) {
3634 memprintf(errmsg, "out of memory");
3635 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003636 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003637 actrule->kw = kw;
3638 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003639
Christopher Faulet61cc8522020-04-20 14:54:42 +02003640 cur_arg++;
3641 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3642 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3643 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003644 }
3645
Christopher Faulet61cc8522020-04-20 14:54:42 +02003646 chk = calloc(1, sizeof(*chk));
3647 if (!chk) {
3648 memprintf(errmsg, "out of memory");
3649 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003650 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003651 chk->action = TCPCHK_ACT_ACTION_KW;
3652 chk->action_kw.rule = actrule;
3653 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003654
3655 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003656 free(actrule);
3657 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003658}
3659
Christopher Faulet61cc8522020-04-20 14:54:42 +02003660/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3661 * returned on error.
3662 */
3663static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3664 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003665{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003666 struct tcpcheck_rule *chk = NULL;
3667 struct sockaddr_storage *sk = NULL;
3668 char *comment = NULL, *sni = NULL, *alpn = NULL;
3669 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003670 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003671 unsigned short conn_opts = 0;
3672 long port = 0;
3673 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003674
Christopher Faulet61cc8522020-04-20 14:54:42 +02003675 list_for_each_entry(chk, rules, list) {
3676 if (chk->action == TCPCHK_ACT_CONNECT)
3677 break;
3678 if (chk->action == TCPCHK_ACT_COMMENT ||
3679 chk->action == TCPCHK_ACT_ACTION_KW ||
3680 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3681 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003682
Christopher Faulet61cc8522020-04-20 14:54:42 +02003683 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003684 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003685 "when there is a 'connect' step in the tcp-check ruleset");
3686 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003687 }
3688
Christopher Faulet61cc8522020-04-20 14:54:42 +02003689 cur_arg++;
3690 while (*(args[cur_arg])) {
3691 if (strcmp(args[cur_arg], "default") == 0)
3692 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3693 else if (strcmp(args[cur_arg], "addr") == 0) {
3694 int port1, port2;
3695 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003696
Christopher Faulet61cc8522020-04-20 14:54:42 +02003697 if (!*(args[cur_arg+1])) {
3698 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3699 goto error;
3700 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003701
Christopher Faulet61cc8522020-04-20 14:54:42 +02003702 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3703 if (!sk) {
3704 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3705 goto error;
3706 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003707
Christopher Faulet61cc8522020-04-20 14:54:42 +02003708 proto = protocol_by_family(sk->ss_family);
3709 if (!proto || !proto->connect) {
3710 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3711 args[cur_arg]);
3712 goto error;
3713 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003714
Christopher Faulet61cc8522020-04-20 14:54:42 +02003715 if (port1 != port2) {
3716 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3717 args[cur_arg], args[cur_arg+1]);
3718 goto error;
3719 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003720
Christopher Faulet61cc8522020-04-20 14:54:42 +02003721 cur_arg++;
3722 }
3723 else if (strcmp(args[cur_arg], "port") == 0) {
3724 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003725
Christopher Faulet61cc8522020-04-20 14:54:42 +02003726 if (!*(args[cur_arg+1])) {
3727 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3728 goto error;
3729 }
3730 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003731
Christopher Faulet61cc8522020-04-20 14:54:42 +02003732 port = 0;
3733 release_sample_expr(port_expr);
3734 p = args[cur_arg]; end = p + strlen(p);
3735 port = read_uint(&p, end);
3736 if (p != end) {
3737 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003738
Christopher Faulet61cc8522020-04-20 14:54:42 +02003739 px->conf.args.ctx = ARGC_SRV;
3740 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3741 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003742
Christopher Faulet61cc8522020-04-20 14:54:42 +02003743 if (!port_expr) {
3744 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3745 goto error;
3746 }
3747 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3748 memprintf(errmsg, "error detected while parsing port expression : "
3749 " fetch method '%s' extracts information from '%s', "
3750 "none of which is available here.\n",
3751 args[cur_arg], sample_src_names(port_expr->fetch->use));
3752 goto error;
3753 }
3754 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3755 }
3756 else if (port > 65535 || port < 1) {
3757 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3758 args[cur_arg]);
3759 goto error;
3760 }
3761 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003762 else if (strcmp(args[cur_arg], "proto") == 0) {
3763 if (!*(args[cur_arg+1])) {
3764 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3765 goto error;
3766 }
3767 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3768 if (!mux_proto) {
3769 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3770 goto error;
3771 }
3772 cur_arg++;
3773 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003774 else if (strcmp(args[cur_arg], "comment") == 0) {
3775 if (!*(args[cur_arg+1])) {
3776 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3777 goto error;
3778 }
3779 cur_arg++;
3780 free(comment);
3781 comment = strdup(args[cur_arg]);
3782 if (!comment) {
3783 memprintf(errmsg, "out of memory");
3784 goto error;
3785 }
3786 }
3787 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3788 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3789 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3790 conn_opts |= TCPCHK_OPT_SOCKS4;
3791 else if (strcmp(args[cur_arg], "linger") == 0)
3792 conn_opts |= TCPCHK_OPT_LINGER;
3793#ifdef USE_OPENSSL
3794 else if (strcmp(args[cur_arg], "ssl") == 0) {
3795 px->options |= PR_O_TCPCHK_SSL;
3796 conn_opts |= TCPCHK_OPT_SSL;
3797 }
3798 else if (strcmp(args[cur_arg], "sni") == 0) {
3799 if (!*(args[cur_arg+1])) {
3800 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3801 goto error;
3802 }
3803 cur_arg++;
3804 free(sni);
3805 sni = strdup(args[cur_arg]);
3806 if (!sni) {
3807 memprintf(errmsg, "out of memory");
3808 goto error;
3809 }
3810 }
3811 else if (strcmp(args[cur_arg], "alpn") == 0) {
3812#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3813 free(alpn);
3814 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3815 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3816 goto error;
3817 }
3818 cur_arg++;
3819#else
3820 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003821 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003822#endif
3823 }
3824#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003825
Christopher Faulet61cc8522020-04-20 14:54:42 +02003826 else {
3827 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3828#ifdef USE_OPENSSL
3829 ", 'ssl', 'sni', 'alpn'"
3830#endif /* USE_OPENSSL */
3831 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3832 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003833 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003834 }
3835 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003836 }
3837
Christopher Faulet61cc8522020-04-20 14:54:42 +02003838 chk = calloc(1, sizeof(*chk));
3839 if (!chk) {
3840 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003841 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003842 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003843 chk->action = TCPCHK_ACT_CONNECT;
3844 chk->comment = comment;
3845 chk->connect.port = port;
3846 chk->connect.options = conn_opts;
3847 chk->connect.sni = sni;
3848 chk->connect.alpn = alpn;
3849 chk->connect.alpn_len= alpn_len;
3850 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003851 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003852 if (sk)
3853 chk->connect.addr = *sk;
3854 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003855
Christopher Faulet61cc8522020-04-20 14:54:42 +02003856 error:
3857 free(alpn);
3858 free(sni);
3859 free(comment);
3860 release_sample_expr(port_expr);
3861 return NULL;
3862}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003863
Christopher Faulet61cc8522020-04-20 14:54:42 +02003864/* Parses and creates a tcp-check send rule. NULL is returned on error */
3865static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3866 const char *file, int line, char **errmsg)
3867{
3868 struct tcpcheck_rule *chk = NULL;
3869 char *comment = NULL, *data = NULL;
3870 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003871
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003872 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3873 type = TCPCHK_SEND_BINARY_LF;
3874 else if (strcmp(args[cur_arg], "send-binary") == 0)
3875 type = TCPCHK_SEND_BINARY;
3876 else if (strcmp(args[cur_arg], "send-lf") == 0)
3877 type = TCPCHK_SEND_STRING_LF;
3878 else if (strcmp(args[cur_arg], "send") == 0)
3879 type = TCPCHK_SEND_STRING;
3880
Christopher Faulet61cc8522020-04-20 14:54:42 +02003881 if (!*(args[cur_arg+1])) {
3882 memprintf(errmsg, "'%s' expects a %s as argument",
3883 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003884 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003885 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003886
Christopher Faulet61cc8522020-04-20 14:54:42 +02003887 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003888
Christopher Faulet61cc8522020-04-20 14:54:42 +02003889 cur_arg += 2;
3890 while (*(args[cur_arg])) {
3891 if (strcmp(args[cur_arg], "comment") == 0) {
3892 if (!*(args[cur_arg+1])) {
3893 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3894 goto error;
3895 }
3896 cur_arg++;
3897 free(comment);
3898 comment = strdup(args[cur_arg]);
3899 if (!comment) {
3900 memprintf(errmsg, "out of memory");
3901 goto error;
3902 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003903 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003904 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003905 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003906 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003907 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003908 }
3909 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003910 }
3911
Christopher Faulet61cc8522020-04-20 14:54:42 +02003912 chk = calloc(1, sizeof(*chk));
3913 if (!chk) {
3914 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003915 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003916 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003917 chk->action = TCPCHK_ACT_SEND;
3918 chk->comment = comment;
3919 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003920
Christopher Faulet61cc8522020-04-20 14:54:42 +02003921 switch (chk->send.type) {
3922 case TCPCHK_SEND_STRING:
3923 chk->send.data = ist2(strdup(data), strlen(data));
3924 if (!isttest(chk->send.data)) {
3925 memprintf(errmsg, "out of memory");
3926 goto error;
3927 }
3928 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003929 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003930 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003931 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003932 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3933 goto error;
3934 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003935 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003936 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003937 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003938 case TCPCHK_SEND_STRING_LF:
3939 case TCPCHK_SEND_BINARY_LF:
3940 LIST_INIT(&chk->send.fmt);
3941 px->conf.args.ctx = ARGC_SRV;
3942 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3943 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3944 goto error;
3945 }
3946 break;
3947 case TCPCHK_SEND_HTTP:
3948 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003949 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003950 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003951
Christopher Faulet61cc8522020-04-20 14:54:42 +02003952 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003953
Christopher Faulet61cc8522020-04-20 14:54:42 +02003954 error:
3955 free(chk);
3956 free(comment);
3957 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003958}
3959
Christopher Faulet61cc8522020-04-20 14:54:42 +02003960/* Parses and creates a http-check send rule. NULL is returned on error */
3961static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3962 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003963{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003964 struct tcpcheck_rule *chk = NULL;
3965 struct tcpcheck_http_hdr *hdr = NULL;
3966 struct http_hdr hdrs[global.tune.max_http_hdr];
3967 char *meth = NULL, *uri = NULL, *vsn = NULL;
3968 char *body = NULL, *comment = NULL;
3969 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003970 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003971
3972 cur_arg++;
3973 while (*(args[cur_arg])) {
3974 if (strcmp(args[cur_arg], "meth") == 0) {
3975 if (!*(args[cur_arg+1])) {
3976 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3977 goto error;
3978 }
3979 cur_arg++;
3980 meth = args[cur_arg];
3981 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003982 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003983 if (!*(args[cur_arg+1])) {
3984 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3985 goto error;
3986 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003987 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
3988 if (strcmp(args[cur_arg], "uri-lf") == 0)
3989 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003990 cur_arg++;
3991 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02003992 }
Christopher Faulet907701b2020-04-28 09:37:00 +02003993 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003994 if (!*(args[cur_arg+1])) {
3995 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3996 goto error;
3997 }
3998 cur_arg++;
3999 vsn = args[cur_arg];
4000 }
4001 else if (strcmp(args[cur_arg], "hdr") == 0) {
4002 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4003 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4004 goto error;
4005 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004006
4007 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4008 if (host_hdr >= 0) {
4009 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4010 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4011 goto error;
4012 }
4013 host_hdr = i;
4014 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004015 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4016 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4017 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4018 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004019
Christopher Faulet61cc8522020-04-20 14:54:42 +02004020 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4021 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4022 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004023 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004024 cur_arg += 2;
4025 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004026 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004027 if (!*(args[cur_arg+1])) {
4028 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4029 goto error;
4030 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004031 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4032 if (strcmp(args[cur_arg], "body-lf") == 0)
4033 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004034 cur_arg++;
4035 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004036 }
4037 else if (strcmp(args[cur_arg], "comment") == 0) {
4038 if (!*(args[cur_arg+1])) {
4039 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4040 goto error;
4041 }
4042 cur_arg++;
4043 free(comment);
4044 comment = strdup(args[cur_arg]);
4045 if (!comment) {
4046 memprintf(errmsg, "out of memory");
4047 goto error;
4048 }
4049 }
4050 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004051 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004052 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004053 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004054 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004055 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004056 }
4057
Christopher Faulet61cc8522020-04-20 14:54:42 +02004058 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004059
Christopher Faulet61cc8522020-04-20 14:54:42 +02004060 chk = calloc(1, sizeof(*chk));
4061 if (!chk) {
4062 memprintf(errmsg, "out of memory");
4063 goto error;
4064 }
4065 chk->action = TCPCHK_ACT_SEND;
4066 chk->comment = comment; comment = NULL;
4067 chk->send.type = TCPCHK_SEND_HTTP;
4068 chk->send.http.flags = flags;
4069 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004070
Christopher Faulet61cc8522020-04-20 14:54:42 +02004071 if (meth) {
4072 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4073 chk->send.http.meth.str.area = strdup(meth);
4074 chk->send.http.meth.str.data = strlen(meth);
4075 if (!chk->send.http.meth.str.area) {
4076 memprintf(errmsg, "out of memory");
4077 goto error;
4078 }
4079 }
4080 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004081 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4082 LIST_INIT(&chk->send.http.uri_fmt);
4083 px->conf.args.ctx = ARGC_SRV;
4084 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4085 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4086 goto error;
4087 }
4088 }
4089 else {
4090 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4091 if (!isttest(chk->send.http.uri)) {
4092 memprintf(errmsg, "out of memory");
4093 goto error;
4094 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004095 }
4096 }
4097 if (vsn) {
4098 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4099 if (!isttest(chk->send.http.vsn)) {
4100 memprintf(errmsg, "out of memory");
4101 goto error;
4102 }
4103 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004104 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004105 hdr = calloc(1, sizeof(*hdr));
4106 if (!hdr) {
4107 memprintf(errmsg, "out of memory");
4108 goto error;
4109 }
4110 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004111 hdr->name = istdup(hdrs[i].n);
4112 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004113 memprintf(errmsg, "out of memory");
4114 goto error;
4115 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004116
Christopher Fauletb61caf42020-04-21 10:57:42 +02004117 ist0(hdrs[i].v);
4118 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 +02004119 goto error;
4120 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4121 hdr = NULL;
4122 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004123
Christopher Faulet61cc8522020-04-20 14:54:42 +02004124 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004125 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4126 LIST_INIT(&chk->send.http.body_fmt);
4127 px->conf.args.ctx = ARGC_SRV;
4128 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4129 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4130 goto error;
4131 }
4132 }
4133 else {
4134 chk->send.http.body = ist2(strdup(body), strlen(body));
4135 if (!isttest(chk->send.http.body)) {
4136 memprintf(errmsg, "out of memory");
4137 goto error;
4138 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004139 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004140 }
4141
Christopher Faulet61cc8522020-04-20 14:54:42 +02004142 return chk;
4143
4144 error:
4145 free_tcpcheck_http_hdr(hdr);
4146 free_tcpcheck(chk, 0);
4147 free(comment);
4148 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004149}
4150
Christopher Faulet61cc8522020-04-20 14:54:42 +02004151/* Parses and creates a http-check comment rule. NULL is returned on error */
4152static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4153 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004154{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004155 struct tcpcheck_rule *chk = NULL;
4156 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004157
Christopher Faulet61cc8522020-04-20 14:54:42 +02004158 if (!*(args[cur_arg+1])) {
4159 memprintf(errmsg, "expects a string as argument");
4160 goto error;
4161 }
4162 cur_arg++;
4163 comment = strdup(args[cur_arg]);
4164 if (!comment) {
4165 memprintf(errmsg, "out of memory");
4166 goto error;
4167 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004168
Christopher Faulet61cc8522020-04-20 14:54:42 +02004169 chk = calloc(1, sizeof(*chk));
4170 if (!chk) {
4171 memprintf(errmsg, "out of memory");
4172 goto error;
4173 }
4174 chk->action = TCPCHK_ACT_COMMENT;
4175 chk->comment = comment;
4176 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004177
Christopher Faulet61cc8522020-04-20 14:54:42 +02004178 error:
4179 free(comment);
4180 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004181}
4182
Christopher Faulet61cc8522020-04-20 14:54:42 +02004183/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4184 * on error. <proto> is set to the right protocol flags (covered by the
4185 * TCPCHK_RULES_PROTO_CHK mask).
4186 */
4187static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4188 struct list *rules, unsigned int proto,
4189 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004190{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004191 struct tcpcheck_rule *prev_check, *chk = NULL;
4192 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004193 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004194 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004195 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4196 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4197 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004198 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004199 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004200 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004201
Christopher Faulet39708192020-05-05 10:47:36 +02004202 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004203 if (!*(args[cur_arg+1])) {
4204 memprintf(errmsg, "expects at least a matching pattern as arguments");
4205 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004206 }
4207
Christopher Faulet61cc8522020-04-20 14:54:42 +02004208 cur_arg++;
4209 while (*(args[cur_arg])) {
4210 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004211
Christopher Faulet61cc8522020-04-20 14:54:42 +02004212 rescan:
4213 if (strcmp(args[cur_arg], "min-recv") == 0) {
4214 if (in_pattern) {
4215 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4216 goto error;
4217 }
4218 if (!*(args[cur_arg+1])) {
4219 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4220 goto error;
4221 }
4222 /* Use an signed integer here because of chksize */
4223 cur_arg++;
4224 min_recv = atol(args[cur_arg]);
4225 if (min_recv < -1 || min_recv > INT_MAX) {
4226 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4227 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004228 }
4229 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004230 else if (*(args[cur_arg]) == '!') {
4231 in_pattern = 1;
4232 while (*(args[cur_arg]) == '!') {
4233 inverse = !inverse;
4234 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004235 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004236 if (!*(args[cur_arg]))
4237 cur_arg++;
4238 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004239 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004240 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4241 if (type != TCPCHK_EXPECT_UNDEF) {
4242 memprintf(errmsg, "only on pattern expected");
4243 goto error;
4244 }
4245 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004246 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004247 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004248 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004249
Christopher Faulet61cc8522020-04-20 14:54:42 +02004250 if (!*(args[cur_arg+1])) {
4251 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4252 goto error;
4253 }
4254 cur_arg++;
4255 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004256 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004257 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4258 if (proto == TCPCHK_RULES_HTTP_CHK)
4259 goto bad_http_kw;
4260 if (type != TCPCHK_EXPECT_UNDEF) {
4261 memprintf(errmsg, "only on pattern expected");
4262 goto error;
4263 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004264 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004265
Christopher Faulet61cc8522020-04-20 14:54:42 +02004266 if (!*(args[cur_arg+1])) {
4267 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4268 goto error;
4269 }
4270 cur_arg++;
4271 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004272 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004273 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4274 if (type != TCPCHK_EXPECT_UNDEF) {
4275 memprintf(errmsg, "only on pattern expected");
4276 goto error;
4277 }
4278 if (proto != TCPCHK_RULES_HTTP_CHK)
4279 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4280 else {
4281 if (*(args[cur_arg]) != 's')
4282 goto bad_http_kw;
4283 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4284 }
4285
4286 if (!*(args[cur_arg+1])) {
4287 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4288 goto error;
4289 }
4290 cur_arg++;
4291 pattern = args[cur_arg];
4292 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004293 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4294 if (proto != TCPCHK_RULES_HTTP_CHK)
4295 goto bad_tcp_kw;
4296 if (type != TCPCHK_EXPECT_UNDEF) {
4297 memprintf(errmsg, "only on pattern expected");
4298 goto error;
4299 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004300 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004301
Christopher Faulet61cc8522020-04-20 14:54:42 +02004302 if (!*(args[cur_arg+1])) {
4303 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4304 goto error;
4305 }
4306 cur_arg++;
4307 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004308 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004309 else if (strcmp(args[cur_arg], "custom") == 0) {
4310 if (in_pattern) {
4311 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4312 goto error;
4313 }
4314 if (type != TCPCHK_EXPECT_UNDEF) {
4315 memprintf(errmsg, "only on pattern expected");
4316 goto error;
4317 }
4318 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004319 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004320 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004321 int orig_arg = cur_arg;
4322
4323 if (proto != TCPCHK_RULES_HTTP_CHK)
4324 goto bad_tcp_kw;
4325 if (type != TCPCHK_EXPECT_UNDEF) {
4326 memprintf(errmsg, "only on pattern expected");
4327 goto error;
4328 }
4329 type = TCPCHK_EXPECT_HTTP_HEADER;
4330
Christopher Fauletb5594262020-05-05 20:23:13 +02004331 if (strcmp(args[cur_arg], "fhdr") == 0)
4332 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4333
Christopher Faulet39708192020-05-05 10:47:36 +02004334 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004335 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4336 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4337 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004338 args[orig_arg]);
4339 goto error;
4340 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004341
4342 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4343 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4344
Christopher Faulet39708192020-05-05 10:47:36 +02004345 cur_arg += 2;
4346 if (strcmp(args[cur_arg], "-m") == 0) {
4347 if (!*(args[cur_arg+1])) {
4348 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4349 args[orig_arg], args[cur_arg]);
4350 goto error;
4351 }
4352 if (strcmp(args[cur_arg+1], "str") == 0)
4353 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4354 else if (strcmp(args[cur_arg+1], "beg") == 0)
4355 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4356 else if (strcmp(args[cur_arg+1], "end") == 0)
4357 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4358 else if (strcmp(args[cur_arg+1], "sub") == 0)
4359 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004360 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4361 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4362 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4363 args[orig_arg]);
4364 goto error;
4365 }
Christopher Faulet39708192020-05-05 10:47:36 +02004366 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004367 }
Christopher Faulet39708192020-05-05 10:47:36 +02004368 else {
4369 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4370 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4371 goto error;
4372 }
4373 cur_arg += 2;
4374 }
4375 else
4376 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4377 npat = args[cur_arg];
4378
Christopher Fauletb5594262020-05-05 20:23:13 +02004379 if (!*(args[cur_arg+1]) ||
4380 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004381 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4382 goto next;
4383 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004384 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4385 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004386
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004387 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004388 if (strcmp(args[cur_arg+2], "-m") == 0) {
4389 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004390 if (!*(args[cur_arg+1])) {
4391 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4392 args[orig_arg], args[cur_arg]);
4393 goto error;
4394 }
4395 if (strcmp(args[cur_arg+1], "str") == 0)
4396 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4397 else if (strcmp(args[cur_arg+1], "beg") == 0)
4398 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4399 else if (strcmp(args[cur_arg+1], "end") == 0)
4400 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4401 else if (strcmp(args[cur_arg+1], "sub") == 0)
4402 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004403 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4404 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4405 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4406 args[orig_arg]);
4407 goto error;
4408 }
Christopher Faulet39708192020-05-05 10:47:36 +02004409 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004410 }
Christopher Faulet39708192020-05-05 10:47:36 +02004411 else {
4412 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4413 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4414 goto error;
4415 }
Christopher Faulet39708192020-05-05 10:47:36 +02004416 }
4417 else
4418 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004419
Christopher Fauletb5594262020-05-05 20:23:13 +02004420 if (!*(args[cur_arg+2])) {
4421 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4422 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004423 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004424 vpat = args[cur_arg+2];
4425 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004426 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004427 else if (strcmp(args[cur_arg], "comment") == 0) {
4428 if (in_pattern) {
4429 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4430 goto error;
4431 }
4432 if (!*(args[cur_arg+1])) {
4433 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4434 goto error;
4435 }
4436 cur_arg++;
4437 free(comment);
4438 comment = strdup(args[cur_arg]);
4439 if (!comment) {
4440 memprintf(errmsg, "out of memory");
4441 goto error;
4442 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004443 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004444 else if (strcmp(args[cur_arg], "on-success") == 0) {
4445 if (in_pattern) {
4446 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4447 goto error;
4448 }
4449 if (!*(args[cur_arg+1])) {
4450 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4451 goto error;
4452 }
4453 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004454 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004455 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004456 else if (strcmp(args[cur_arg], "on-error") == 0) {
4457 if (in_pattern) {
4458 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4459 goto error;
4460 }
4461 if (!*(args[cur_arg+1])) {
4462 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4463 goto error;
4464 }
4465 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004466 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004467 }
4468 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4469 if (in_pattern) {
4470 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4471 goto error;
4472 }
4473 if (!*(args[cur_arg+1])) {
4474 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4475 goto error;
4476 }
4477 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4478 ok_st = HCHK_STATUS_L7OKD;
4479 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4480 ok_st = HCHK_STATUS_L7OKCD;
4481 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4482 ok_st = HCHK_STATUS_L6OK;
4483 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4484 ok_st = HCHK_STATUS_L4OK;
4485 else {
4486 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4487 args[cur_arg], args[cur_arg+1]);
4488 goto error;
4489 }
4490 cur_arg++;
4491 }
4492 else if (strcmp(args[cur_arg], "error-status") == 0) {
4493 if (in_pattern) {
4494 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4495 goto error;
4496 }
4497 if (!*(args[cur_arg+1])) {
4498 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4499 goto error;
4500 }
4501 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4502 err_st = HCHK_STATUS_L7RSP;
4503 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4504 err_st = HCHK_STATUS_L7STS;
4505 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4506 err_st = HCHK_STATUS_L6RSP;
4507 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4508 err_st = HCHK_STATUS_L4CON;
4509 else {
4510 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4511 args[cur_arg], args[cur_arg+1]);
4512 goto error;
4513 }
4514 cur_arg++;
4515 }
4516 else if (strcmp(args[cur_arg], "status-code") == 0) {
4517 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004518
Christopher Faulet61cc8522020-04-20 14:54:42 +02004519 if (in_pattern) {
4520 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4521 goto error;
4522 }
4523 if (!*(args[cur_arg+1])) {
4524 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4525 goto error;
4526 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004527
Christopher Faulet61cc8522020-04-20 14:54:42 +02004528 cur_arg++;
4529 release_sample_expr(status_expr);
4530 px->conf.args.ctx = ARGC_SRV;
4531 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4532 file, line, errmsg, &px->conf.args, NULL);
4533 if (!status_expr) {
4534 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4535 goto error;
4536 }
4537 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4538 memprintf(errmsg, "error detected while parsing status-code expression : "
4539 " fetch method '%s' extracts information from '%s', "
4540 "none of which is available here.\n",
4541 args[cur_arg], sample_src_names(status_expr->fetch->use));
4542 goto error;
4543 }
4544 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4545 }
4546 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4547 if (in_pattern) {
4548 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4549 goto error;
4550 }
4551 if (!*(args[cur_arg+1])) {
4552 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4553 goto error;
4554 }
4555 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4556 tout_st = HCHK_STATUS_L7TOUT;
4557 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4558 tout_st = HCHK_STATUS_L6TOUT;
4559 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4560 tout_st = HCHK_STATUS_L4TOUT;
4561 else {
4562 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4563 args[cur_arg], args[cur_arg+1]);
4564 goto error;
4565 }
4566 cur_arg++;
4567 }
4568 else {
4569 if (proto == TCPCHK_RULES_HTTP_CHK) {
4570 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004571 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004572 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004573 }
4574 else {
4575 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004576 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4577 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004578 }
4579 goto error;
4580 }
Christopher Faulet39708192020-05-05 10:47:36 +02004581 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004582 cur_arg++;
4583 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004584
Christopher Faulet61cc8522020-04-20 14:54:42 +02004585 chk = calloc(1, sizeof(*chk));
4586 if (!chk) {
4587 memprintf(errmsg, "out of memory");
4588 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004589 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004590 chk->action = TCPCHK_ACT_EXPECT;
4591 LIST_INIT(&chk->expect.onerror_fmt);
4592 LIST_INIT(&chk->expect.onsuccess_fmt);
4593 chk->comment = comment; comment = NULL;
4594 chk->expect.type = type;
4595 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004596 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004597 chk->expect.ok_status = ok_st;
4598 chk->expect.err_status = err_st;
4599 chk->expect.tout_status = tout_st;
4600 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004601
Christopher Faulet61cc8522020-04-20 14:54:42 +02004602 if (on_success_msg) {
4603 px->conf.args.ctx = ARGC_SRV;
4604 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4605 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4606 goto error;
4607 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004608 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004609 if (on_error_msg) {
4610 px->conf.args.ctx = ARGC_SRV;
4611 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4612 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4613 goto error;
4614 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004615 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004616
Christopher Faulet61cc8522020-04-20 14:54:42 +02004617 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004618 case TCPCHK_EXPECT_HTTP_STATUS: {
4619 const char *p = pattern;
4620 unsigned int c1,c2;
4621
4622 chk->expect.codes.codes = NULL;
4623 chk->expect.codes.num = 0;
4624 while (1) {
4625 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4626 if (*p == '-') {
4627 p++;
4628 c2 = read_uint(&p, pattern + strlen(pattern));
4629 }
4630 if (c1 > c2) {
4631 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4632 goto error;
4633 }
4634
4635 chk->expect.codes.num++;
4636 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4637 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4638 if (!chk->expect.codes.codes) {
4639 memprintf(errmsg, "out of memory");
4640 goto error;
4641 }
4642 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4643 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4644
4645 if (*p == '\0')
4646 break;
4647 if (*p != ',') {
4648 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4649 goto error;
4650 }
4651 p++;
4652 }
4653 break;
4654 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004655 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004656 case TCPCHK_EXPECT_HTTP_BODY:
4657 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004658 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004659 memprintf(errmsg, "out of memory");
4660 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004661 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004662 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004663 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004664 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004665
4666 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004667 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4668 goto error;
4669 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004670 chk->expect.data.len = len;
4671 break;
4672 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004673 case TCPCHK_EXPECT_STRING_REGEX:
4674 case TCPCHK_EXPECT_BINARY_REGEX:
4675 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4676 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004677 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004678 if (!chk->expect.regex)
4679 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004680 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004681
4682 case TCPCHK_EXPECT_STRING_LF:
4683 case TCPCHK_EXPECT_BINARY_LF:
4684 case TCPCHK_EXPECT_HTTP_BODY_LF:
4685 LIST_INIT(&chk->expect.fmt);
4686 px->conf.args.ctx = ARGC_SRV;
4687 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4688 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4689 goto error;
4690 }
4691 break;
4692
Christopher Faulet39708192020-05-05 10:47:36 +02004693 case TCPCHK_EXPECT_HTTP_HEADER:
4694 if (!npat) {
4695 memprintf(errmsg, "unexpected error, undefined header name pattern");
4696 goto error;
4697 }
4698 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4699 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4700 if (!chk->expect.hdr.name_re)
4701 goto error;
4702 }
4703 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4704 px->conf.args.ctx = ARGC_SRV;
4705 LIST_INIT(&chk->expect.hdr.name_fmt);
4706 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4707 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4708 goto error;
4709 }
4710 }
4711 else {
4712 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4713 if (!isttest(chk->expect.hdr.name)) {
4714 memprintf(errmsg, "out of memory");
4715 goto error;
4716 }
4717 }
4718
4719 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4720 chk->expect.hdr.value = IST_NULL;
4721 break;
4722 }
4723
4724 if (!vpat) {
4725 memprintf(errmsg, "unexpected error, undefined header value pattern");
4726 goto error;
4727 }
4728 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4729 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4730 if (!chk->expect.hdr.value_re)
4731 goto error;
4732 }
4733 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4734 px->conf.args.ctx = ARGC_SRV;
4735 LIST_INIT(&chk->expect.hdr.value_fmt);
4736 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4737 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4738 goto error;
4739 }
4740 }
4741 else {
4742 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4743 if (!isttest(chk->expect.hdr.value)) {
4744 memprintf(errmsg, "out of memory");
4745 goto error;
4746 }
4747 }
4748
Christopher Faulet61cc8522020-04-20 14:54:42 +02004749 break;
4750 case TCPCHK_EXPECT_CUSTOM:
4751 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4752 break;
4753 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004754 memprintf(errmsg, "pattern not found");
4755 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004756 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004757
Christopher Faulet61cc8522020-04-20 14:54:42 +02004758 /* All tcp-check expect points back to the first inverse expect rule in
4759 * a chain of one or more expect rule, potentially itself.
4760 */
4761 chk->expect.head = chk;
4762 list_for_each_entry_rev(prev_check, rules, list) {
4763 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4764 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4765 chk->expect.head = prev_check;
4766 continue;
4767 }
4768 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4769 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004770 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004771 return chk;
4772
4773 error:
4774 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004775 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004776 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004777 return NULL;
4778}
4779
Christopher Faulet61cc8522020-04-20 14:54:42 +02004780/* Overwrites fields of the old http send rule with those of the new one. When
4781 * replaced, old values are freed and replaced by the new ones. New values are
4782 * not copied but transferred. At the end <new> should be empty and can be
4783 * safely released. This function never fails.
4784 */
4785static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004786{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004787 struct logformat_node *lf, *lfb;
4788 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004789
Christopher Faulet404f9192020-04-09 23:13:54 +02004790
Christopher Faulet61cc8522020-04-20 14:54:42 +02004791 if (new->send.http.meth.str.area) {
4792 free(old->send.http.meth.str.area);
4793 old->send.http.meth.meth = new->send.http.meth.meth;
4794 old->send.http.meth.str.area = new->send.http.meth.str.area;
4795 old->send.http.meth.str.data = new->send.http.meth.str.data;
4796 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004797 }
4798
Christopher Faulet61cc8522020-04-20 14:54:42 +02004799 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4800 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004801 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004802 else
4803 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4804 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4805 old->send.http.uri = new->send.http.uri;
4806 new->send.http.uri = IST_NULL;
4807 }
4808 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4809 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004810 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004811 else
4812 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4813 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4814 LIST_INIT(&old->send.http.uri_fmt);
4815 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4816 LIST_DEL(&lf->list);
4817 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4818 }
4819 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004820
Christopher Faulet61cc8522020-04-20 14:54:42 +02004821 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004822 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004823 old->send.http.vsn = new->send.http.vsn;
4824 new->send.http.vsn = IST_NULL;
4825 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004826
Christopher Faulet61cc8522020-04-20 14:54:42 +02004827 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4828 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4829 LIST_DEL(&hdr->list);
4830 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004831 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004832
4833 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4834 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004835 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004836 else
4837 free_tcpcheck_fmt(&old->send.http.body_fmt);
4838 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4839 old->send.http.body = new->send.http.body;
4840 new->send.http.body = IST_NULL;
4841 }
4842 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4843 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004844 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004845 else
4846 free_tcpcheck_fmt(&old->send.http.body_fmt);
4847 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4848 LIST_INIT(&old->send.http.body_fmt);
4849 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4850 LIST_DEL(&lf->list);
4851 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4852 }
4853 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004854}
4855
Christopher Faulet61cc8522020-04-20 14:54:42 +02004856/* Internal function used to add an http-check rule in a list during the config
4857 * parsing step. Depending on its type, and the previously inserted rules, a
4858 * specific action may be performed or an error may be reported. This functions
4859 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4860 * message.
4861 */
4862static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004863{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004864 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004865
Christopher Faulet61cc8522020-04-20 14:54:42 +02004866 /* the implicit send rule coming from an "option httpchk" line must be
4867 * merged with the first explici http-check send rule, if
4868 * any. Depdending the declaration order some tests are required.
4869 *
4870 * Some tests is also required for other kinds of http-check rules to be
4871 * sure the ruleset remains valid.
4872 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004873
Christopher Faulet61cc8522020-04-20 14:54:42 +02004874 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004875 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004876 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4877 * following tests are performed :
4878 *
4879 * 1- If there is no such rule or if it is not a send rule, the implicit send
4880 * rule is pushed in front of the ruleset
4881 *
4882 * 2- If it is another implicit send rule, it is replaced with the new one.
4883 *
4884 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004885 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004886 * new send rule (the implicit one).
4887 */
4888 r = get_first_tcpcheck_rule(rules);
4889 if (r && r->action == TCPCHK_ACT_CONNECT)
4890 r = get_next_tcpcheck_rule(rules, r);
4891 if (!r || r->action != TCPCHK_ACT_SEND)
4892 LIST_ADD(rules->list, &chk->list);
4893 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4894 LIST_DEL(&r->list);
4895 free_tcpcheck(r, 0);
4896 LIST_ADD(rules->list, &chk->list);
4897 }
4898 else {
4899 tcpcheck_overwrite_send_http_rule(r, chk);
4900 free_tcpcheck(chk, 0);
4901 }
4902 }
4903 else {
4904 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4905 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4906 * with an existing implicit send rule, if any. At the end, if there is no error,
4907 * the rule is appended to the list.
4908 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004909
Christopher Faulet61cc8522020-04-20 14:54:42 +02004910 r = get_last_tcpcheck_rule(rules);
4911 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4912 /* no error */;
4913 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4914 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4915 chk->index+1);
4916 return 0;
4917 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004918 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004919 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4920 chk->index+1);
4921 return 0;
4922 }
4923 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4924 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4925 chk->index+1);
4926 return 0;
4927 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004928
Christopher Faulet61cc8522020-04-20 14:54:42 +02004929 if (chk->action == TCPCHK_ACT_SEND) {
4930 r = get_first_tcpcheck_rule(rules);
4931 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4932 tcpcheck_overwrite_send_http_rule(r, chk);
4933 free_tcpcheck(chk, 0);
4934 LIST_DEL(&r->list);
4935 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4936 chk = r;
4937 }
4938 }
4939 LIST_ADDQ(rules->list, &chk->list);
4940 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004941 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004942}
4943
Christopher Faulet61cc8522020-04-20 14:54:42 +02004944/**************************************************************************/
4945/************************** Init/deinit checks ****************************/
4946/**************************************************************************/
4947static const char *init_check(struct check *check, int type)
4948{
4949 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004950
Christopher Faulet61cc8522020-04-20 14:54:42 +02004951 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4952 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004953
Christopher Faulet61cc8522020-04-20 14:54:42 +02004954 check->bi.area = calloc(check->bi.size, sizeof(char));
4955 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004956
Christopher Faulet61cc8522020-04-20 14:54:42 +02004957 if (!check->bi.area || !check->bo.area)
4958 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004959
Christopher Faulet61cc8522020-04-20 14:54:42 +02004960 check->wait_list.tasklet = tasklet_new();
4961 if (!check->wait_list.tasklet)
4962 return "out of memory while allocating check tasklet";
4963 check->wait_list.events = 0;
4964 check->wait_list.tasklet->process = event_srv_chk_io;
4965 check->wait_list.tasklet->context = check;
4966 return NULL;
4967}
4968
4969void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004970{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004971 task_destroy(check->task);
4972 if (check->wait_list.tasklet)
4973 tasklet_free(check->wait_list.tasklet);
4974
4975 free(check->bi.area);
4976 free(check->bo.area);
4977 if (check->cs) {
4978 free(check->cs->conn);
4979 check->cs->conn = NULL;
4980 cs_free(check->cs);
4981 check->cs = NULL;
4982 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004983}
4984
Christopher Faulet61cc8522020-04-20 14:54:42 +02004985/* manages a server health-check. Returns the time the task accepts to wait, or
4986 * TIME_ETERNITY for infinity.
4987 */
4988static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004989{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004990 struct check *check = context;
4991
4992 if (check->type == PR_O2_EXT_CHK)
4993 return process_chk_proc(t, context, state);
4994 return process_chk_conn(t, context, state);
4995
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004996}
4997
Christopher Faulet61cc8522020-04-20 14:54:42 +02004998
4999static int start_check_task(struct check *check, int mininter,
5000 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005001{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005002 struct task *t;
5003 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005004
Christopher Faulet61cc8522020-04-20 14:54:42 +02005005 if (check->type == PR_O2_EXT_CHK)
5006 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005007
Christopher Faulet61cc8522020-04-20 14:54:42 +02005008 /* task for the check */
5009 if ((t = task_new(thread_mask)) == NULL) {
5010 ha_alert("Starting [%s:%s] check: out of memory.\n",
5011 check->server->proxy->id, check->server->id);
5012 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005013 }
5014
Christopher Faulet61cc8522020-04-20 14:54:42 +02005015 check->task = t;
5016 t->process = process_chk;
5017 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005018
Christopher Faulet61cc8522020-04-20 14:54:42 +02005019 if (mininter < srv_getinter(check))
5020 mininter = srv_getinter(check);
5021
5022 if (global.max_spread_checks && mininter > global.max_spread_checks)
5023 mininter = global.max_spread_checks;
5024
5025 /* check this every ms */
5026 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5027 check->start = now;
5028 task_queue(t);
5029
5030 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005031}
5032
Christopher Faulet61cc8522020-04-20 14:54:42 +02005033/* updates the server's weight during a warmup stage. Once the final weight is
5034 * reached, the task automatically stops. Note that any server status change
5035 * must have updated s->last_change accordingly.
5036 */
5037static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005038{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005039 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005040
Christopher Faulet61cc8522020-04-20 14:54:42 +02005041 /* by default, plan on stopping the task */
5042 t->expire = TICK_ETERNITY;
5043 if ((s->next_admin & SRV_ADMF_MAINT) ||
5044 (s->next_state != SRV_ST_STARTING))
5045 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005046
Christopher Faulet61cc8522020-04-20 14:54:42 +02005047 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005048
Christopher Faulet61cc8522020-04-20 14:54:42 +02005049 /* recalculate the weights and update the state */
5050 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005051
Christopher Faulet61cc8522020-04-20 14:54:42 +02005052 /* probably that we can refill this server with a bit more connections */
5053 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005054
Christopher Faulet61cc8522020-04-20 14:54:42 +02005055 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005056
Christopher Faulet61cc8522020-04-20 14:54:42 +02005057 /* get back there in 1 second or 1/20th of the slowstart interval,
5058 * whichever is greater, resulting in small 5% steps.
5059 */
5060 if (s->next_state == SRV_ST_STARTING)
5061 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5062 return t;
5063}
5064
5065/*
5066 * Start health-check.
5067 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5068 */
5069static int start_checks()
5070{
5071
5072 struct proxy *px;
5073 struct server *s;
5074 struct task *t;
5075 int nbcheck=0, mininter=0, srvpos=0;
5076
5077 /* 0- init the dummy frontend used to create all checks sessions */
5078 init_new_proxy(&checks_fe);
5079 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5080 checks_fe.mode = PR_MODE_TCP;
5081 checks_fe.maxconn = 0;
5082 checks_fe.conn_retries = CONN_RETRIES;
5083 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5084 checks_fe.timeout.client = TICK_ETERNITY;
5085
5086 /* 1- count the checkers to run simultaneously.
5087 * We also determine the minimum interval among all of those which
5088 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5089 * will be used to spread their start-up date. Those which have
5090 * a shorter interval will start independently and will not dictate
5091 * too short an interval for all others.
5092 */
5093 for (px = proxies_list; px; px = px->next) {
5094 for (s = px->srv; s; s = s->next) {
5095 if (s->slowstart) {
5096 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5097 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5098 return ERR_ALERT | ERR_FATAL;
5099 }
5100 /* We need a warmup task that will be called when the server
5101 * state switches from down to up.
5102 */
5103 s->warmup = t;
5104 t->process = server_warmup;
5105 t->context = s;
5106 /* server can be in this state only because of */
5107 if (s->next_state == SRV_ST_STARTING)
5108 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 +02005109 }
5110
Christopher Faulet61cc8522020-04-20 14:54:42 +02005111 if (s->check.state & CHK_ST_CONFIGURED) {
5112 nbcheck++;
5113 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5114 (!mininter || mininter > srv_getinter(&s->check)))
5115 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005116 }
5117
Christopher Faulet61cc8522020-04-20 14:54:42 +02005118 if (s->agent.state & CHK_ST_CONFIGURED) {
5119 nbcheck++;
5120 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5121 (!mininter || mininter > srv_getinter(&s->agent)))
5122 mininter = srv_getinter(&s->agent);
5123 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005124 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005125 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005126
Christopher Faulet61cc8522020-04-20 14:54:42 +02005127 if (!nbcheck)
5128 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005129
Christopher Faulet61cc8522020-04-20 14:54:42 +02005130 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005131
Christopher Faulet61cc8522020-04-20 14:54:42 +02005132 /*
5133 * 2- start them as far as possible from each others. For this, we will
5134 * start them after their interval set to the min interval divided by
5135 * the number of servers, weighted by the server's position in the list.
5136 */
5137 for (px = proxies_list; px; px = px->next) {
5138 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5139 if (init_pid_list()) {
5140 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5141 return ERR_ALERT | ERR_FATAL;
5142 }
5143 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005144
Christopher Faulet61cc8522020-04-20 14:54:42 +02005145 for (s = px->srv; s; s = s->next) {
5146 /* A task for the main check */
5147 if (s->check.state & CHK_ST_CONFIGURED) {
5148 if (s->check.type == PR_O2_EXT_CHK) {
5149 if (!prepare_external_check(&s->check))
5150 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005151 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005152 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5153 return ERR_ALERT | ERR_FATAL;
5154 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005155 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005156
Christopher Faulet61cc8522020-04-20 14:54:42 +02005157 /* A task for a auxiliary agent check */
5158 if (s->agent.state & CHK_ST_CONFIGURED) {
5159 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5160 return ERR_ALERT | ERR_FATAL;
5161 }
5162 srvpos++;
5163 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005164 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005165 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005166 return 0;
5167}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005168
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005169
Christopher Faulet61cc8522020-04-20 14:54:42 +02005170/*
5171 * Return value:
5172 * the port to be used for the health check
5173 * 0 in case no port could be found for the check
5174 */
5175static int srv_check_healthcheck_port(struct check *chk)
5176{
5177 int i = 0;
5178 struct server *srv = NULL;
5179
5180 srv = chk->server;
5181
5182 /* by default, we use the health check port ocnfigured */
5183 if (chk->port > 0)
5184 return chk->port;
5185
5186 /* try to get the port from check_core.addr if check.port not set */
5187 i = get_host_port(&chk->addr);
5188 if (i > 0)
5189 return i;
5190
5191 /* try to get the port from server address */
5192 /* prevent MAPPORTS from working at this point, since checks could
5193 * not be performed in such case (MAPPORTS impose a relative ports
5194 * based on live traffic)
5195 */
5196 if (srv->flags & SRV_F_MAPPORTS)
5197 return 0;
5198
5199 i = srv->svc_port; /* by default */
5200 if (i > 0)
5201 return i;
5202
5203 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005204}
5205
Christopher Faulet61cc8522020-04-20 14:54:42 +02005206/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5207 * if an error occurred.
5208 */
5209static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005210{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005211 const char *err;
5212 struct tcpcheck_rule *r;
5213 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005214
Christopher Faulet61cc8522020-04-20 14:54:42 +02005215 if (!srv->do_check)
5216 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005217
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005218
Christopher Faulet61cc8522020-04-20 14:54:42 +02005219 /* If neither a port nor an addr was specified and no check transport
5220 * layer is forced, then the transport layer used by the checks is the
5221 * same as for the production traffic. Otherwise we use raw_sock by
5222 * default, unless one is specified.
5223 */
5224 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5225 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5226 srv->check.use_ssl = srv->use_ssl;
5227 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005228 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005229 else if (srv->check.use_ssl == 1)
5230 srv->check.xprt = xprt_get(XPRT_SSL);
5231 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005232 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02005233 else if (srv->check.use_ssl == 1)
5234 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005235
Christopher Faulet12882cf2020-04-23 15:50:18 +02005236 /* Inherit the mux protocol from the server if not already defined for
5237 * the check
5238 */
5239 if (srv->mux_proto && !srv->check.mux_proto)
5240 srv->check.mux_proto = srv->mux_proto;
5241
Christopher Faulet61cc8522020-04-20 14:54:42 +02005242 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005243
Christopher Faulet61cc8522020-04-20 14:54:42 +02005244 /* We need at least a service port, a check port or the first tcp-check
5245 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5246 */
5247 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5248 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5249 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005250
Christopher Faulet61cc8522020-04-20 14:54:42 +02005251 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5252 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5253 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5254 ret |= ERR_ALERT | ERR_ABORT;
5255 goto out;
5256 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005257
Christopher Faulet61cc8522020-04-20 14:54:42 +02005258 /* search the first action (connect / send / expect) in the list */
5259 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5260 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5261 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5262 "nor tcp_check rule 'connect' with port information.\n",
5263 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5264 ret |= ERR_ALERT | ERR_ABORT;
5265 goto out;
5266 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005267
Christopher Faulet61cc8522020-04-20 14:54:42 +02005268 /* scan the tcp-check ruleset to ensure a port has been configured */
5269 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5270 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5271 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5272 "and a tcp_check rule 'connect' with no port information.\n",
5273 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5274 ret |= ERR_ALERT | ERR_ABORT;
5275 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005276 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005277 }
5278
Christopher Faulet61cc8522020-04-20 14:54:42 +02005279 init:
5280 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5281 struct tcpcheck_ruleset *rs = NULL;
5282 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5283 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005284
Christopher Faulet61cc8522020-04-20 14:54:42 +02005285 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5286 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005287
Christopher Faulet61cc8522020-04-20 14:54:42 +02005288 rs = find_tcpcheck_ruleset("*tcp-check");
5289 if (!rs) {
5290 rs = create_tcpcheck_ruleset("*tcp-check");
5291 if (rs == NULL) {
5292 ha_alert("config: %s '%s': out of memory.\n",
5293 proxy_type_str(srv->proxy), srv->proxy->id);
5294 ret |= ERR_ALERT | ERR_FATAL;
5295 goto out;
5296 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005297 }
5298
Christopher Faulet61cc8522020-04-20 14:54:42 +02005299 free_tcpcheck_vars(&rules->preset_vars);
5300 rules->list = &rs->rules;
5301 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005302 }
5303
Christopher Faulet61cc8522020-04-20 14:54:42 +02005304 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5305 if (err) {
5306 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5307 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5308 ret |= ERR_ALERT | ERR_ABORT;
5309 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005310 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005311 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5312 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005313
Christopher Faulet61cc8522020-04-20 14:54:42 +02005314 out:
5315 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005316}
5317
Christopher Faulet61cc8522020-04-20 14:54:42 +02005318/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5319 * if an error occurred.
5320 */
5321static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005322{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005323 struct tcpcheck_rule *chk;
5324 const char *err;
5325 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005326
Christopher Faulet61cc8522020-04-20 14:54:42 +02005327 if (!srv->do_agent)
5328 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005329
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005330 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005331 * implicit one is inserted before all others.
5332 */
5333 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5334 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5335 chk = calloc(1, sizeof(*chk));
5336 if (!chk) {
5337 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5338 " to agent-check for server '%s' (out of memory).\n",
5339 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5340 ret |= ERR_ALERT | ERR_FATAL;
5341 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005342 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005343 chk->action = TCPCHK_ACT_CONNECT;
5344 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5345 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005346 }
5347
Christopher Faulete5870d82020-04-15 11:32:03 +02005348
Christopher Faulet61cc8522020-04-20 14:54:42 +02005349 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5350 if (err) {
5351 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5352 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5353 ret |= ERR_ALERT | ERR_ABORT;
5354 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005355 }
5356
Christopher Faulet61cc8522020-04-20 14:54:42 +02005357 if (!srv->agent.inter)
5358 srv->agent.inter = srv->check.inter;
5359
5360 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5361 global.maxsock++;
5362
5363 out:
5364 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005365}
5366
Christopher Faulet61cc8522020-04-20 14:54:42 +02005367/* Check tcp-check health-check configuration for the proxy <px>. */
5368static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005369{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005370 struct tcpcheck_rule *chk, *back;
5371 char *comment = NULL, *errmsg = NULL;
5372 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5373 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005374
Christopher Faulet61cc8522020-04-20 14:54:42 +02005375 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5376 deinit_proxy_tcpcheck(px);
5377 goto out;
5378 }
5379
5380 free(px->check_command);
5381 free(px->check_path);
5382 px->check_command = px->check_path = NULL;
5383
5384 if (!px->tcpcheck_rules.list) {
5385 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5386 ret |= ERR_ALERT | ERR_FATAL;
5387 goto out;
5388 }
5389
5390 /* HTTP ruleset only : */
5391 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5392 struct tcpcheck_rule *next;
5393
5394 /* move remaining implicit send rule from "option httpchk" line to the right place.
5395 * If such rule exists, it must be the first one. In this case, the rule is moved
5396 * after the first connect rule, if any. Otherwise, nothing is done.
5397 */
5398 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5399 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5400 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5401 if (next && next->action == TCPCHK_ACT_CONNECT) {
5402 LIST_DEL(&chk->list);
5403 LIST_ADD(&next->list, &chk->list);
5404 chk->index = next->index;
5405 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005406 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005407
5408 /* add implicit expect rule if the last one is a send. It is inherited from previous
5409 * versions where the http expect rule was optional. Now it is possible to chained
5410 * send/expect rules but the last expect may still be implicit.
5411 */
5412 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5413 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005414 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005415 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5416 px->conf.file, px->conf.line, &errmsg);
5417 if (!next) {
5418 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5419 "(%s).\n", px->id, errmsg);
5420 free(errmsg);
5421 ret |= ERR_ALERT | ERR_FATAL;
5422 goto out;
5423 }
5424 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5425 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005426 }
5427 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005428
5429 /* For all ruleset: */
5430
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005431 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005432 * implicit one is inserted before all others.
5433 */
5434 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5435 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5436 chk = calloc(1, sizeof(*chk));
5437 if (!chk) {
5438 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5439 "(out of memory).\n", px->id);
5440 ret |= ERR_ALERT | ERR_FATAL;
5441 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005442 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005443 chk->action = TCPCHK_ACT_CONNECT;
5444 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5445 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5446 }
5447
5448 /* Remove all comment rules. To do so, when a such rule is found, the
5449 * comment is assigned to the following rule(s).
5450 */
5451 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5452 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5453 free(comment);
5454 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005455 }
5456
Christopher Faulet61cc8522020-04-20 14:54:42 +02005457 prev_action = chk->action;
5458 switch (chk->action) {
5459 case TCPCHK_ACT_COMMENT:
5460 free(comment);
5461 comment = chk->comment;
5462 LIST_DEL(&chk->list);
5463 free(chk);
5464 break;
5465 case TCPCHK_ACT_CONNECT:
5466 if (!chk->comment && comment)
5467 chk->comment = strdup(comment);
5468 /* fall though */
5469 case TCPCHK_ACT_ACTION_KW:
5470 free(comment);
5471 comment = NULL;
5472 break;
5473 case TCPCHK_ACT_SEND:
5474 case TCPCHK_ACT_EXPECT:
5475 if (!chk->comment && comment)
5476 chk->comment = strdup(comment);
5477 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005478 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005479 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005480 free(comment);
5481 comment = NULL;
5482
5483 out:
5484 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005485}
5486
Christopher Faulet61cc8522020-04-20 14:54:42 +02005487void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005488{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005489 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5490 px->tcpcheck_rules.flags = 0;
5491 px->tcpcheck_rules.list = NULL;
5492}
Christopher Faulete5870d82020-04-15 11:32:03 +02005493
Christopher Faulet61cc8522020-04-20 14:54:42 +02005494static void deinit_srv_check(struct server *srv)
5495{
5496 if (srv->check.state & CHK_ST_CONFIGURED)
5497 free_check(&srv->check);
5498 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5499 srv->do_check = 0;
5500}
Christopher Faulete5870d82020-04-15 11:32:03 +02005501
Christopher Faulet61cc8522020-04-20 14:54:42 +02005502
5503static void deinit_srv_agent_check(struct server *srv)
5504{
5505 if (srv->agent.tcpcheck_rules) {
5506 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5507 free(srv->agent.tcpcheck_rules);
5508 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005509 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005510
Christopher Faulet61cc8522020-04-20 14:54:42 +02005511 if (srv->agent.state & CHK_ST_CONFIGURED)
5512 free_check(&srv->agent);
5513
5514 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5515 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005516}
5517
Christopher Faulet61cc8522020-04-20 14:54:42 +02005518static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005519{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005520 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005521 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005522 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005523
Christopher Fauletd7cee712020-04-21 13:45:00 +02005524 node = ebpt_first(&shared_tcpchecks);
5525 while (node) {
5526 next = ebpt_next(node);
5527 ebpt_delete(node);
5528 free(node->key);
5529 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005530 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5531 LIST_DEL(&r->list);
5532 free_tcpcheck(r, 0);
5533 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005534 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005535 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005536 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005537}
Christopher Faulete5870d82020-04-15 11:32:03 +02005538
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005539
Christopher Faulet61cc8522020-04-20 14:54:42 +02005540REGISTER_POST_SERVER_CHECK(init_srv_check);
5541REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5542REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5543REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005544
Christopher Faulet61cc8522020-04-20 14:54:42 +02005545REGISTER_SERVER_DEINIT(deinit_srv_check);
5546REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5547REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5548REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005549
Christopher Faulet61cc8522020-04-20 14:54:42 +02005550/**************************************************************************/
5551/****************************** Email alerts ******************************/
5552/* NOTE: It may be pertinent to use an applet to handle email alerts */
5553/* instead of a tcp-check ruleset */
5554/**************************************************************************/
5555void email_alert_free(struct email_alert *alert)
5556{
5557 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005558
Christopher Faulet61cc8522020-04-20 14:54:42 +02005559 if (!alert)
5560 return;
5561
5562 if (alert->rules.list) {
5563 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5564 LIST_DEL(&rule->list);
5565 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005566 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005567 free_tcpcheck_vars(&alert->rules.preset_vars);
5568 free(alert->rules.list);
5569 alert->rules.list = NULL;
5570 }
5571 pool_free(pool_head_email_alert, alert);
5572}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005573
Christopher Faulet61cc8522020-04-20 14:54:42 +02005574static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5575{
5576 struct check *check = context;
5577 struct email_alertq *q;
5578 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005579
Christopher Faulet61cc8522020-04-20 14:54:42 +02005580 q = container_of(check, typeof(*q), check);
5581
5582 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5583 while (1) {
5584 if (!(check->state & CHK_ST_ENABLED)) {
5585 if (LIST_ISEMPTY(&q->email_alerts)) {
5586 /* All alerts processed, queue the task */
5587 t->expire = TICK_ETERNITY;
5588 task_queue(t);
5589 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005590 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005591
5592 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5593 LIST_DEL(&alert->list);
5594 t->expire = now_ms;
5595 check->tcpcheck_rules = &alert->rules;
5596 check->status = HCHK_STATUS_INI;
5597 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005598 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005599
5600 process_chk(t, context, state);
5601 if (check->state & CHK_ST_INPROGRESS)
5602 break;
5603
5604 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5605 email_alert_free(alert);
5606 check->tcpcheck_rules = NULL;
5607 check->server = NULL;
5608 check->state &= ~CHK_ST_ENABLED;
5609 }
5610 end:
5611 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5612 return t;
5613}
5614
5615/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5616 *
5617 * The function returns 1 in success case, otherwise, it returns 0 and err is
5618 * filled.
5619 */
5620int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5621{
5622 struct mailer *mailer;
5623 struct email_alertq *queues;
5624 const char *err_str;
5625 int i = 0;
5626
5627 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5628 memprintf(err, "out of memory while allocating mailer alerts queues");
5629 goto fail_no_queue;
5630 }
5631
5632 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5633 struct email_alertq *q = &queues[i];
5634 struct check *check = &q->check;
5635 struct task *t;
5636
5637 LIST_INIT(&q->email_alerts);
5638 HA_SPIN_INIT(&q->lock);
5639 check->inter = mls->timeout.mail;
5640 check->rise = DEF_AGENT_RISETIME;
5641 check->proxy = p;
5642 check->fall = DEF_AGENT_FALLTIME;
5643 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5644 memprintf(err, "%s", err_str);
5645 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005646 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005647
5648 check->xprt = mailer->xprt;
5649 check->addr = mailer->addr;
5650 check->port = get_host_port(&mailer->addr);
5651
5652 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5653 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005654 goto error;
5655 }
5656
Christopher Faulet61cc8522020-04-20 14:54:42 +02005657 check->task = t;
5658 t->process = process_email_alert;
5659 t->context = check;
5660
5661 /* check this in one ms */
5662 t->expire = TICK_ETERNITY;
5663 check->start = now;
5664 task_queue(t);
5665 }
5666
5667 mls->users++;
5668 free(p->email_alert.mailers.name);
5669 p->email_alert.mailers.m = mls;
5670 p->email_alert.queues = queues;
5671 return 0;
5672
5673 error:
5674 for (i = 0; i < mls->count; i++) {
5675 struct email_alertq *q = &queues[i];
5676 struct check *check = &q->check;
5677
5678 free_check(check);
5679 }
5680 free(queues);
5681 fail_no_queue:
5682 return 1;
5683}
5684
5685static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5686{
5687 struct tcpcheck_rule *tcpcheck, *prev_check;
5688 struct tcpcheck_expect *expect;
5689
5690 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5691 return 0;
5692 memset(tcpcheck, 0, sizeof(*tcpcheck));
5693 tcpcheck->action = TCPCHK_ACT_EXPECT;
5694
5695 expect = &tcpcheck->expect;
5696 expect->type = TCPCHK_EXPECT_STRING;
5697 LIST_INIT(&expect->onerror_fmt);
5698 LIST_INIT(&expect->onsuccess_fmt);
5699 expect->ok_status = HCHK_STATUS_L7OKD;
5700 expect->err_status = HCHK_STATUS_L7RSP;
5701 expect->tout_status = HCHK_STATUS_L7TOUT;
5702 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005703 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005704 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5705 return 0;
5706 }
5707
5708 /* All tcp-check expect points back to the first inverse expect rule
5709 * in a chain of one or more expect rule, potentially itself.
5710 */
5711 tcpcheck->expect.head = tcpcheck;
5712 list_for_each_entry_rev(prev_check, rules->list, list) {
5713 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5714 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5715 tcpcheck->expect.head = prev_check;
5716 continue;
5717 }
5718 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5719 break;
5720 }
5721 LIST_ADDQ(rules->list, &tcpcheck->list);
5722 return 1;
5723}
5724
5725static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5726{
5727 struct tcpcheck_rule *tcpcheck;
5728 struct tcpcheck_send *send;
5729 const char *in;
5730 char *dst;
5731 int i;
5732
5733 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5734 return 0;
5735 memset(tcpcheck, 0, sizeof(*tcpcheck));
5736 tcpcheck->action = TCPCHK_ACT_SEND;
5737
5738 send = &tcpcheck->send;
5739 send->type = TCPCHK_SEND_STRING;
5740
5741 for (i = 0; strs[i]; i++)
5742 send->data.len += strlen(strs[i]);
5743
Christopher Fauletb61caf42020-04-21 10:57:42 +02005744 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005745 if (!isttest(send->data)) {
5746 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5747 return 0;
5748 }
5749
Christopher Fauletb61caf42020-04-21 10:57:42 +02005750 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005751 for (i = 0; strs[i]; i++)
5752 for (in = strs[i]; (*dst = *in++); dst++);
5753 *dst = 0;
5754
5755 LIST_ADDQ(rules->list, &tcpcheck->list);
5756 return 1;
5757}
5758
5759static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5760 struct email_alertq *q, const char *msg)
5761{
5762 struct email_alert *alert;
5763 struct tcpcheck_rule *tcpcheck;
5764 struct check *check = &q->check;
5765
5766 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5767 goto error;
5768 LIST_INIT(&alert->list);
5769 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5770 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5771 if (!alert->rules.list)
5772 goto error;
5773 LIST_INIT(alert->rules.list);
5774 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5775 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005776
Christopher Faulet61cc8522020-04-20 14:54:42 +02005777 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5778 goto error;
5779 memset(tcpcheck, 0, sizeof(*tcpcheck));
5780 tcpcheck->action = TCPCHK_ACT_CONNECT;
5781 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005782
Christopher Faulet61cc8522020-04-20 14:54:42 +02005783 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005784
Christopher Faulet61cc8522020-04-20 14:54:42 +02005785 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005786 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005787
5788 {
5789 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5790 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5791 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005792 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005793
Christopher Faulet61cc8522020-04-20 14:54:42 +02005794 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5795 goto error;
5796
5797 {
5798 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5799 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005800 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005801 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005802
5803 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5804 goto error;
5805
5806 {
5807 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5808 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005809 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005810 }
5811
Christopher Faulet61cc8522020-04-20 14:54:42 +02005812 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5813 goto error;
5814
5815 {
5816 const char * const strs[2] = { "DATA\r\n" };
5817 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005818 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005819 }
5820
5821 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5822 goto error;
5823
5824 {
5825 struct tm tm;
5826 char datestr[48];
5827 const char * const strs[18] = {
5828 "From: ", p->email_alert.from, "\r\n",
5829 "To: ", p->email_alert.to, "\r\n",
5830 "Date: ", datestr, "\r\n",
5831 "Subject: [HAproxy Alert] ", msg, "\r\n",
5832 "\r\n",
5833 msg, "\r\n",
5834 "\r\n",
5835 ".\r\n",
5836 NULL
5837 };
5838
5839 get_localtime(date.tv_sec, &tm);
5840
5841 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005842 goto error;
5843 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005844
5845 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005846 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005847 }
5848
5849 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005850 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005851
5852 {
5853 const char * const strs[2] = { "QUIT\r\n" };
5854 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5855 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005856 }
5857
Christopher Faulet61cc8522020-04-20 14:54:42 +02005858 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5859 goto error;
5860
5861 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5862 task_wakeup(check->task, TASK_WOKEN_MSG);
5863 LIST_ADDQ(&q->email_alerts, &alert->list);
5864 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5865 return 1;
5866
5867error:
5868 email_alert_free(alert);
5869 return 0;
5870}
5871
5872static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5873{
5874 int i;
5875 struct mailer *mailer;
5876
5877 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5878 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5879 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5880 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5881 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005882 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005883 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005884
Christopher Faulet61cc8522020-04-20 14:54:42 +02005885 return;
5886}
5887
5888/*
5889 * Send email alert if configured.
5890 */
5891void send_email_alert(struct server *s, int level, const char *format, ...)
5892{
5893 va_list argp;
5894 char buf[1024];
5895 int len;
5896 struct proxy *p = s->proxy;
5897
5898 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5899 return;
5900
5901 va_start(argp, format);
5902 len = vsnprintf(buf, sizeof(buf), format, argp);
5903 va_end(argp);
5904
5905 if (len < 0 || len >= sizeof(buf)) {
5906 ha_alert("Email alert [%s] could not format message\n", p->id);
5907 return;
5908 }
5909
5910 enqueue_email_alert(p, s, buf);
5911}
5912
5913/**************************************************************************/
5914/************************** Check sample fetches **************************/
5915/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005916
Christopher Faulet61cc8522020-04-20 14:54:42 +02005917static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005918 { /* END */ },
5919}};
5920
5921INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5922
5923
5924/**************************************************************************/
5925/************************ Check's parsing functions ***********************/
5926/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005927/* Parses the "tcp-check" proxy keyword */
5928static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5929 struct proxy *defpx, const char *file, int line,
5930 char **errmsg)
5931{
Christopher Faulet404f9192020-04-09 23:13:54 +02005932 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005933 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005934 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005935
5936 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5937 ret = 1;
5938
Christopher Faulet404f9192020-04-09 23:13:54 +02005939 /* Deduce the ruleset name from the proxy info */
5940 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5941 ((curpx == defpx) ? "defaults" : curpx->id),
5942 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005943
Christopher Faulet61cc8522020-04-20 14:54:42 +02005944 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005945 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005946 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005947 if (rs == NULL) {
5948 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005949 goto error;
5950 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005951 }
5952
Gaetan Rivet5301b012020-02-25 17:19:17 +01005953 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005954 if (!LIST_ISEMPTY(&rs->rules)) {
5955 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005956 index = chk->index + 1;
5957 }
5958
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005959 cur_arg = 1;
5960 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005961 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005962 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5963 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005964 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005965 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005966 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005967 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005968 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005969 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005970 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5971
5972 if (!kw) {
5973 action_kw_tcp_check_build_list(&trash);
5974 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5975 "%s%s. but got '%s'",
5976 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5977 goto error;
5978 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005979 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005980 }
5981
5982 if (!chk) {
5983 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5984 goto error;
5985 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005986 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005987
5988 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005989 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005990 LIST_ADDQ(&rs->rules, &chk->list);
5991
5992 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005993 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005994 /* Use this ruleset if the proxy already has tcp-check enabled */
5995 curpx->tcpcheck_rules.list = &rs->rules;
5996 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5997 }
5998 else {
5999 /* mark this ruleset as unused for now */
6000 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
6001 }
6002
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006003 return ret;
6004
6005 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006006 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006007 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006008 return -1;
6009}
6010
Christopher Faulet51b129f2020-04-09 15:54:18 +02006011/* Parses the "http-check" proxy keyword */
6012static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6013 struct proxy *defpx, const char *file, int line,
6014 char **errmsg)
6015{
Christopher Faulete5870d82020-04-15 11:32:03 +02006016 struct tcpcheck_ruleset *rs = NULL;
6017 struct tcpcheck_rule *chk = NULL;
6018 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006019
6020 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6021 ret = 1;
6022
6023 cur_arg = 1;
6024 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6025 /* enable a graceful server shutdown on an HTTP 404 response */
6026 curpx->options |= PR_O_DISABLE404;
6027 if (too_many_args(1, args, errmsg, NULL))
6028 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006029 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006030 }
6031 else if (strcmp(args[cur_arg], "send-state") == 0) {
6032 /* enable emission of the apparent state of a server in HTTP checks */
6033 curpx->options2 |= PR_O2_CHK_SNDST;
6034 if (too_many_args(1, args, errmsg, NULL))
6035 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006036 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006037 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006038
Christopher Faulete5870d82020-04-15 11:32:03 +02006039 /* Deduce the ruleset name from the proxy info */
6040 chunk_printf(&trash, "*http-check-%s_%s-%d",
6041 ((curpx == defpx) ? "defaults" : curpx->id),
6042 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006043
Christopher Faulet61cc8522020-04-20 14:54:42 +02006044 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006045 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006046 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006047 if (rs == NULL) {
6048 memprintf(errmsg, "out of memory.\n");
6049 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006050 }
6051 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006052
Christopher Faulete5870d82020-04-15 11:32:03 +02006053 index = 0;
6054 if (!LIST_ISEMPTY(&rs->rules)) {
6055 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6056 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6057 index = chk->index + 1;
6058 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006059
Christopher Faulete5870d82020-04-15 11:32:03 +02006060 if (strcmp(args[cur_arg], "connect") == 0)
6061 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6062 else if (strcmp(args[cur_arg], "send") == 0)
6063 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6064 else if (strcmp(args[cur_arg], "expect") == 0)
6065 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6066 file, line, errmsg);
6067 else if (strcmp(args[cur_arg], "comment") == 0)
6068 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6069 else {
6070 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006071
Christopher Faulete5870d82020-04-15 11:32:03 +02006072 if (!kw) {
6073 action_kw_tcp_check_build_list(&trash);
6074 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6075 " 'send', 'expect'%s%s. but got '%s'",
6076 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6077 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006078 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006079 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6080 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006081
Christopher Faulete5870d82020-04-15 11:32:03 +02006082 if (!chk) {
6083 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6084 goto error;
6085 }
6086 ret = (*errmsg != NULL); /* Handle warning */
6087
6088 chk->index = index;
6089 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6090 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6091 /* Use this ruleset if the proxy already has http-check enabled */
6092 curpx->tcpcheck_rules.list = &rs->rules;
6093 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6094 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6095 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6096 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006097 goto error;
6098 }
6099 }
6100 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006101 /* mark this ruleset as unused for now */
6102 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6103 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006104 }
6105
Christopher Faulete5870d82020-04-15 11:32:03 +02006106 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006107 return ret;
6108
6109 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006110 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006111 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006112 return -1;
6113}
6114
Christopher Faulete9111b62020-04-09 18:12:08 +02006115/* Parses the "external-check" proxy keyword */
6116static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6117 struct proxy *defpx, const char *file, int line,
6118 char **errmsg)
6119{
6120 int cur_arg, ret = 0;
6121
6122 cur_arg = 1;
6123 if (!*(args[cur_arg])) {
6124 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6125 goto error;
6126 }
6127
6128 if (strcmp(args[cur_arg], "command") == 0) {
6129 if (too_many_args(2, args, errmsg, NULL))
6130 goto error;
6131 if (!*(args[cur_arg+1])) {
6132 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6133 goto error;
6134 }
6135 free(curpx->check_command);
6136 curpx->check_command = strdup(args[cur_arg+1]);
6137 }
6138 else if (strcmp(args[cur_arg], "path") == 0) {
6139 if (too_many_args(2, args, errmsg, NULL))
6140 goto error;
6141 if (!*(args[cur_arg+1])) {
6142 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6143 goto error;
6144 }
6145 free(curpx->check_path);
6146 curpx->check_path = strdup(args[cur_arg+1]);
6147 }
6148 else {
6149 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6150 args[0], args[1]);
6151 goto error;
6152 }
6153
6154 ret = (*errmsg != NULL); /* Handle warning */
6155 return ret;
6156
6157error:
6158 return -1;
6159}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006160
Christopher Faulet430e4802020-04-09 15:28:16 +02006161/* Parses the "option tcp-check" proxy keyword */
6162int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6163 const char *file, int line)
6164{
Christopher Faulet404f9192020-04-09 23:13:54 +02006165 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006166 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6167 int err_code = 0;
6168
6169 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6170 err_code |= ERR_WARN;
6171
6172 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6173 goto out;
6174
Christopher Faulet404f9192020-04-09 23:13:54 +02006175 curpx->options2 &= ~PR_O2_CHK_ANY;
6176 curpx->options2 |= PR_O2_TCPCHK_CHK;
6177
Christopher Fauletd7e63962020-04-17 20:15:59 +02006178 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006179 /* If a tcp-check rulesset is already set, do nothing */
6180 if (rules->list)
6181 goto out;
6182
6183 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6184 * get it.
6185 */
6186 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6187 goto curpx_ruleset;
6188
6189 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6190 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006191 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006192 if (rs)
6193 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006194 }
6195
Christopher Faulet404f9192020-04-09 23:13:54 +02006196 curpx_ruleset:
6197 /* Deduce the ruleset name from the proxy info */
6198 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6199 ((curpx == defpx) ? "defaults" : curpx->id),
6200 curpx->conf.file, curpx->conf.line);
6201
Christopher Faulet61cc8522020-04-20 14:54:42 +02006202 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006203 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006204 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006205 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006206 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6207 goto error;
6208 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006209 }
6210
Christopher Faulet404f9192020-04-09 23:13:54 +02006211 ruleset_found:
6212 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006213 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006214 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006215
6216 out:
6217 return err_code;
6218
6219 error:
6220 err_code |= ERR_ALERT | ERR_FATAL;
6221 goto out;
6222}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006223
6224/* Parses the "option redis-check" proxy keyword */
6225int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6226 const char *file, int line)
6227{
6228 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6229 static char *redis_res = "+PONG\r\n";
6230
6231 struct tcpcheck_ruleset *rs = NULL;
6232 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6233 struct tcpcheck_rule *chk;
6234 char *errmsg = NULL;
6235 int err_code = 0;
6236
6237 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6238 err_code |= ERR_WARN;
6239
6240 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6241 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006242
6243 curpx->options2 &= ~PR_O2_CHK_ANY;
6244 curpx->options2 |= PR_O2_TCPCHK_CHK;
6245
6246 free_tcpcheck_vars(&rules->preset_vars);
6247 rules->list = NULL;
6248 rules->flags = 0;
6249
Christopher Faulet61cc8522020-04-20 14:54:42 +02006250 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006251 if (rs)
6252 goto ruleset_found;
6253
Christopher Faulet61cc8522020-04-20 14:54:42 +02006254 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006255 if (rs == NULL) {
6256 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6257 goto error;
6258 }
6259
6260 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6261 1, curpx, &rs->rules, file, line, &errmsg);
6262 if (!chk) {
6263 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6264 goto error;
6265 }
6266 chk->index = 0;
6267 LIST_ADDQ(&rs->rules, &chk->list);
6268
6269 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6270 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006271 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006272 "on-success", "Redis server is ok",
6273 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006274 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006275 if (!chk) {
6276 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6277 goto error;
6278 }
6279 chk->index = 1;
6280 LIST_ADDQ(&rs->rules, &chk->list);
6281
Christopher Faulet33f05df2020-04-01 11:08:50 +02006282 ruleset_found:
6283 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006284 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006285
6286 out:
6287 free(errmsg);
6288 return err_code;
6289
6290 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006291 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006292 err_code |= ERR_ALERT | ERR_FATAL;
6293 goto out;
6294}
6295
Christopher Faulet811f78c2020-04-01 11:10:27 +02006296
6297/* Parses the "option ssl-hello-chk" proxy keyword */
6298int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6299 const char *file, int line)
6300{
6301 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6302 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6303 *
6304 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6305 */
6306 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006307 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006308 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6309 "0079" /* ContentLength : 0x79 bytes after this one */
6310 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6311 "000075" /* HandshakeLength : 0x75 bytes after this one */
6312 "0300" /* Hello Version : 0x0300 = v3 */
6313 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6314 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6315 "00" /* Session ID length : empty (no session ID) */
6316 "004E" /* Cipher Suite Length : 78 bytes after this one */
6317 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6318 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6319 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6320 "000D" "000E" "000F" "0010" /* various bit lengths, */
6321 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6322 "0015" "0016" "0017" "0018"
6323 "0019" "001A" "001B" "002F"
6324 "0030" "0031" "0032" "0033"
6325 "0034" "0035" "0036" "0037"
6326 "0038" "0039" "003A"
6327 "01" /* Compression Length : 0x01 = 1 byte for types */
6328 "00" /* Compression Type : 0x00 = NULL compression */
6329 };
6330
6331 struct tcpcheck_ruleset *rs = NULL;
6332 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6333 struct tcpcheck_rule *chk;
6334 char *errmsg = NULL;
6335 int err_code = 0;
6336
6337 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6338 err_code |= ERR_WARN;
6339
6340 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6341 goto out;
6342
Christopher Faulet811f78c2020-04-01 11:10:27 +02006343 curpx->options2 &= ~PR_O2_CHK_ANY;
6344 curpx->options2 |= PR_O2_TCPCHK_CHK;
6345
6346 free_tcpcheck_vars(&rules->preset_vars);
6347 rules->list = NULL;
6348 rules->flags = 0;
6349
Christopher Faulet61cc8522020-04-20 14:54:42 +02006350 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006351 if (rs)
6352 goto ruleset_found;
6353
Christopher Faulet61cc8522020-04-20 14:54:42 +02006354 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006355 if (rs == NULL) {
6356 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6357 goto error;
6358 }
6359
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006360 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006361 1, curpx, &rs->rules, file, line, &errmsg);
6362 if (!chk) {
6363 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6364 goto error;
6365 }
6366 chk->index = 0;
6367 LIST_ADDQ(&rs->rules, &chk->list);
6368
6369 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006370 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006371 "error-status", "L6RSP", "tout-status", "L6TOUT",
6372 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006373 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006374 if (!chk) {
6375 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6376 goto error;
6377 }
6378 chk->index = 1;
6379 LIST_ADDQ(&rs->rules, &chk->list);
6380
Christopher Faulet811f78c2020-04-01 11:10:27 +02006381 ruleset_found:
6382 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006383 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006384
6385 out:
6386 free(errmsg);
6387 return err_code;
6388
6389 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006390 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006391 err_code |= ERR_ALERT | ERR_FATAL;
6392 goto out;
6393}
6394
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006395/* Parses the "option smtpchk" proxy keyword */
6396int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6397 const char *file, int line)
6398{
6399 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6400
6401 struct tcpcheck_ruleset *rs = NULL;
6402 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6403 struct tcpcheck_rule *chk;
6404 struct tcpcheck_var *var = NULL;
6405 char *cmd = NULL, *errmsg = NULL;
6406 int err_code = 0;
6407
6408 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6409 err_code |= ERR_WARN;
6410
6411 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6412 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006413
6414 curpx->options2 &= ~PR_O2_CHK_ANY;
6415 curpx->options2 |= PR_O2_TCPCHK_CHK;
6416
6417 free_tcpcheck_vars(&rules->preset_vars);
6418 rules->list = NULL;
6419 rules->flags = 0;
6420
6421 cur_arg += 2;
6422 if (*args[cur_arg] && *args[cur_arg+1] &&
6423 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6424 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6425 if (cmd)
6426 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6427 }
6428 else {
6429 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6430 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6431 cmd = strdup("HELO localhost");
6432 }
6433
Christopher Fauletb61caf42020-04-21 10:57:42 +02006434 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006435 if (cmd == NULL || var == NULL) {
6436 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6437 goto error;
6438 }
6439 var->data.type = SMP_T_STR;
6440 var->data.u.str.area = cmd;
6441 var->data.u.str.data = strlen(cmd);
6442 LIST_INIT(&var->list);
6443 LIST_ADDQ(&rules->preset_vars, &var->list);
6444 cmd = NULL;
6445 var = NULL;
6446
Christopher Faulet61cc8522020-04-20 14:54:42 +02006447 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006448 if (rs)
6449 goto ruleset_found;
6450
Christopher Faulet61cc8522020-04-20 14:54:42 +02006451 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006452 if (rs == NULL) {
6453 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6454 goto error;
6455 }
6456
6457 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6458 1, curpx, &rs->rules, file, line, &errmsg);
6459 if (!chk) {
6460 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6461 goto error;
6462 }
6463 chk->index = 0;
6464 LIST_ADDQ(&rs->rules, &chk->list);
6465
6466 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6467 "min-recv", "4",
6468 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006469 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006470 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006471 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006472 if (!chk) {
6473 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6474 goto error;
6475 }
6476 chk->index = 1;
6477 LIST_ADDQ(&rs->rules, &chk->list);
6478
6479 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6480 "min-recv", "4",
6481 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006482 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6483 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006484 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006485 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006486 if (!chk) {
6487 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6488 goto error;
6489 }
6490 chk->index = 2;
6491 LIST_ADDQ(&rs->rules, &chk->list);
6492
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006493 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006494 1, curpx, &rs->rules, file, line, &errmsg);
6495 if (!chk) {
6496 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6497 goto error;
6498 }
6499 chk->index = 3;
6500 LIST_ADDQ(&rs->rules, &chk->list);
6501
6502 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6503 "min-recv", "4",
6504 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006505 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6506 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6507 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006508 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006509 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006510 if (!chk) {
6511 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6512 goto error;
6513 }
6514 chk->index = 4;
6515 LIST_ADDQ(&rs->rules, &chk->list);
6516
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006517 ruleset_found:
6518 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006519 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006520
6521 out:
6522 free(errmsg);
6523 return err_code;
6524
6525 error:
6526 free(cmd);
6527 free(var);
6528 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006529 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006530 err_code |= ERR_ALERT | ERR_FATAL;
6531 goto out;
6532}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006533
Christopher Fauletce355072020-04-02 11:44:39 +02006534/* Parses the "option pgsql-check" proxy keyword */
6535int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6536 const char *file, int line)
6537{
6538 static char pgsql_req[] = {
6539 "%[var(check.plen),htonl,hex]" /* The packet length*/
6540 "00030000" /* the version 3.0 */
6541 "7573657200" /* "user" key */
6542 "%[var(check.username),hex]00" /* the username */
6543 "00"
6544 };
6545
6546 struct tcpcheck_ruleset *rs = NULL;
6547 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6548 struct tcpcheck_rule *chk;
6549 struct tcpcheck_var *var = NULL;
6550 char *user = NULL, *errmsg = NULL;
6551 size_t packetlen = 0;
6552 int err_code = 0;
6553
6554 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6555 err_code |= ERR_WARN;
6556
6557 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6558 goto out;
6559
Christopher Fauletce355072020-04-02 11:44:39 +02006560 curpx->options2 &= ~PR_O2_CHK_ANY;
6561 curpx->options2 |= PR_O2_TCPCHK_CHK;
6562
6563 free_tcpcheck_vars(&rules->preset_vars);
6564 rules->list = NULL;
6565 rules->flags = 0;
6566
6567 cur_arg += 2;
6568 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6569 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6570 file, line, args[0], args[1]);
6571 goto error;
6572 }
6573 if (strcmp(args[cur_arg], "user") == 0) {
6574 packetlen = 15 + strlen(args[cur_arg+1]);
6575 user = strdup(args[cur_arg+1]);
6576
Christopher Fauletb61caf42020-04-21 10:57:42 +02006577 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006578 if (user == NULL || var == NULL) {
6579 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6580 goto error;
6581 }
6582 var->data.type = SMP_T_STR;
6583 var->data.u.str.area = user;
6584 var->data.u.str.data = strlen(user);
6585 LIST_INIT(&var->list);
6586 LIST_ADDQ(&rules->preset_vars, &var->list);
6587 user = NULL;
6588 var = NULL;
6589
Christopher Fauletb61caf42020-04-21 10:57:42 +02006590 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006591 if (var == NULL) {
6592 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6593 goto error;
6594 }
6595 var->data.type = SMP_T_SINT;
6596 var->data.u.sint = packetlen;
6597 LIST_INIT(&var->list);
6598 LIST_ADDQ(&rules->preset_vars, &var->list);
6599 var = NULL;
6600 }
6601 else {
6602 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6603 file, line, args[0], args[1]);
6604 goto error;
6605 }
6606
Christopher Faulet61cc8522020-04-20 14:54:42 +02006607 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006608 if (rs)
6609 goto ruleset_found;
6610
Christopher Faulet61cc8522020-04-20 14:54:42 +02006611 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006612 if (rs == NULL) {
6613 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6614 goto error;
6615 }
6616
6617 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6618 1, curpx, &rs->rules, file, line, &errmsg);
6619 if (!chk) {
6620 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6621 goto error;
6622 }
6623 chk->index = 0;
6624 LIST_ADDQ(&rs->rules, &chk->list);
6625
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006626 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006627 1, curpx, &rs->rules, file, line, &errmsg);
6628 if (!chk) {
6629 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6630 goto error;
6631 }
6632 chk->index = 1;
6633 LIST_ADDQ(&rs->rules, &chk->list);
6634
6635 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6636 "min-recv", "5",
6637 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006638 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006639 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006640 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006641 if (!chk) {
6642 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6643 goto error;
6644 }
6645 chk->index = 2;
6646 LIST_ADDQ(&rs->rules, &chk->list);
6647
Christopher Fauletb841c742020-04-27 18:29:49 +02006648 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 +02006649 "min-recv", "9",
6650 "error-status", "L7STS",
6651 "on-success", "PostgreSQL server is ok",
6652 "on-error", "PostgreSQL unknown error",
6653 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006654 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006655 if (!chk) {
6656 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6657 goto error;
6658 }
6659 chk->index = 3;
6660 LIST_ADDQ(&rs->rules, &chk->list);
6661
Christopher Fauletce355072020-04-02 11:44:39 +02006662 ruleset_found:
6663 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006664 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006665
6666 out:
6667 free(errmsg);
6668 return err_code;
6669
6670 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006671 free(user);
6672 free(var);
6673 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006674 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006675 err_code |= ERR_ALERT | ERR_FATAL;
6676 goto out;
6677}
6678
6679
6680/* Parses the "option mysql-check" proxy keyword */
6681int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6682 const char *file, int line)
6683{
6684 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6685 * const char mysql40_client_auth_pkt[] = {
6686 * "\x0e\x00\x00" // packet length
6687 * "\x01" // packet number
6688 * "\x00\x00" // client capabilities
6689 * "\x00\x00\x01" // max packet
6690 * "haproxy\x00" // username (null terminated string)
6691 * "\x00" // filler (always 0x00)
6692 * "\x01\x00\x00" // packet length
6693 * "\x00" // packet number
6694 * "\x01" // COM_QUIT command
6695 * };
6696 */
6697 static char mysql40_rsname[] = "*mysql40-check";
6698 static char mysql40_req[] = {
6699 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6700 "0080" /* client capabilities */
6701 "000001" /* max packet */
6702 "%[var(check.username),hex]00" /* the username */
6703 "00" /* filler (always 0x00) */
6704 "010000" /* packet length*/
6705 "00" /* sequence ID */
6706 "01" /* COM_QUIT command */
6707 };
6708
6709 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6710 * const char mysql41_client_auth_pkt[] = {
6711 * "\x0e\x00\x00\" // packet length
6712 * "\x01" // packet number
6713 * "\x00\x00\x00\x00" // client capabilities
6714 * "\x00\x00\x00\x01" // max packet
6715 * "\x21" // character set (UTF-8)
6716 * char[23] // All zeroes
6717 * "haproxy\x00" // username (null terminated string)
6718 * "\x00" // filler (always 0x00)
6719 * "\x01\x00\x00" // packet length
6720 * "\x00" // packet number
6721 * "\x01" // COM_QUIT command
6722 * };
6723 */
6724 static char mysql41_rsname[] = "*mysql41-check";
6725 static char mysql41_req[] = {
6726 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6727 "00820000" /* client capabilities */
6728 "00800001" /* max packet */
6729 "21" /* character set (UTF-8) */
6730 "000000000000000000000000" /* 23 bytes, al zeroes */
6731 "0000000000000000000000"
6732 "%[var(check.username),hex]00" /* the username */
6733 "00" /* filler (always 0x00) */
6734 "010000" /* packet length*/
6735 "00" /* sequence ID */
6736 "01" /* COM_QUIT command */
6737 };
6738
6739 struct tcpcheck_ruleset *rs = NULL;
6740 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6741 struct tcpcheck_rule *chk;
6742 struct tcpcheck_var *var = NULL;
6743 char *mysql_rsname = "*mysql-check";
6744 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6745 int index = 0, err_code = 0;
6746
6747 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6748 err_code |= ERR_WARN;
6749
6750 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6751 goto out;
6752
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006753 curpx->options2 &= ~PR_O2_CHK_ANY;
6754 curpx->options2 |= PR_O2_TCPCHK_CHK;
6755
6756 free_tcpcheck_vars(&rules->preset_vars);
6757 rules->list = NULL;
6758 rules->flags = 0;
6759
6760 cur_arg += 2;
6761 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006762 int packetlen, userlen;
6763
6764 if (strcmp(args[cur_arg], "user") != 0) {
6765 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6766 file, line, args[0], args[1], args[cur_arg]);
6767 goto error;
6768 }
6769
6770 if (*(args[cur_arg+1]) == 0) {
6771 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6772 file, line, args[0], args[1], args[cur_arg]);
6773 goto error;
6774 }
6775
6776 hdr = calloc(4, sizeof(*hdr));
6777 user = strdup(args[cur_arg+1]);
6778 userlen = strlen(args[cur_arg+1]);
6779
6780 if (hdr == NULL || user == NULL) {
6781 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6782 goto error;
6783 }
6784
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006785 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006786 packetlen = userlen + 7 + 27;
6787 mysql_req = mysql41_req;
6788 mysql_rsname = mysql41_rsname;
6789 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006790 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006791 packetlen = userlen + 7;
6792 mysql_req = mysql40_req;
6793 mysql_rsname = mysql40_rsname;
6794 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006795 else {
6796 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
6797 file, line, args[cur_arg], args[cur_arg+2]);
6798 goto error;
6799 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006800
6801 hdr[0] = (unsigned char)(packetlen & 0xff);
6802 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6803 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6804 hdr[3] = 1;
6805
Christopher Fauletb61caf42020-04-21 10:57:42 +02006806 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006807 if (var == NULL) {
6808 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6809 goto error;
6810 }
6811 var->data.type = SMP_T_STR;
6812 var->data.u.str.area = hdr;
6813 var->data.u.str.data = 4;
6814 LIST_INIT(&var->list);
6815 LIST_ADDQ(&rules->preset_vars, &var->list);
6816 hdr = NULL;
6817 var = NULL;
6818
Christopher Fauletb61caf42020-04-21 10:57:42 +02006819 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006820 if (var == NULL) {
6821 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6822 goto error;
6823 }
6824 var->data.type = SMP_T_STR;
6825 var->data.u.str.area = user;
6826 var->data.u.str.data = strlen(user);
6827 LIST_INIT(&var->list);
6828 LIST_ADDQ(&rules->preset_vars, &var->list);
6829 user = NULL;
6830 var = NULL;
6831 }
6832
Christopher Faulet61cc8522020-04-20 14:54:42 +02006833 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006834 if (rs)
6835 goto ruleset_found;
6836
Christopher Faulet61cc8522020-04-20 14:54:42 +02006837 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006838 if (rs == NULL) {
6839 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6840 goto error;
6841 }
6842
6843 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6844 1, curpx, &rs->rules, file, line, &errmsg);
6845 if (!chk) {
6846 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6847 goto error;
6848 }
6849 chk->index = index++;
6850 LIST_ADDQ(&rs->rules, &chk->list);
6851
6852 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006853 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006854 1, curpx, &rs->rules, file, line, &errmsg);
6855 if (!chk) {
6856 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6857 goto error;
6858 }
6859 chk->index = index++;
6860 LIST_ADDQ(&rs->rules, &chk->list);
6861 }
6862
6863 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006864 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006865 if (!chk) {
6866 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6867 goto error;
6868 }
6869 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6870 chk->index = index++;
6871 LIST_ADDQ(&rs->rules, &chk->list);
6872
6873 if (mysql_req) {
6874 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006875 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006876 if (!chk) {
6877 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6878 goto error;
6879 }
6880 chk->expect.custom = tcpcheck_mysql_expect_ok;
6881 chk->index = index++;
6882 LIST_ADDQ(&rs->rules, &chk->list);
6883 }
6884
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006885 ruleset_found:
6886 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006887 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006888
6889 out:
6890 free(errmsg);
6891 return err_code;
6892
6893 error:
6894 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006895 free(user);
6896 free(var);
6897 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006898 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006899 err_code |= ERR_ALERT | ERR_FATAL;
6900 goto out;
6901}
6902
Christopher Faulet1997eca2020-04-03 23:13:50 +02006903int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6904 const char *file, int line)
6905{
6906 static char *ldap_req = "300C020101600702010304008000";
6907
6908 struct tcpcheck_ruleset *rs = NULL;
6909 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6910 struct tcpcheck_rule *chk;
6911 char *errmsg = NULL;
6912 int err_code = 0;
6913
6914 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6915 err_code |= ERR_WARN;
6916
6917 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6918 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006919
6920 curpx->options2 &= ~PR_O2_CHK_ANY;
6921 curpx->options2 |= PR_O2_TCPCHK_CHK;
6922
6923 free_tcpcheck_vars(&rules->preset_vars);
6924 rules->list = NULL;
6925 rules->flags = 0;
6926
Christopher Faulet61cc8522020-04-20 14:54:42 +02006927 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006928 if (rs)
6929 goto ruleset_found;
6930
Christopher Faulet61cc8522020-04-20 14:54:42 +02006931 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006932 if (rs == NULL) {
6933 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6934 goto error;
6935 }
6936
6937 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6938 1, curpx, &rs->rules, file, line, &errmsg);
6939 if (!chk) {
6940 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6941 goto error;
6942 }
6943 chk->index = 0;
6944 LIST_ADDQ(&rs->rules, &chk->list);
6945
6946 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6947 "min-recv", "14",
6948 "on-error", "Not LDAPv3 protocol",
6949 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006950 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006951 if (!chk) {
6952 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6953 goto error;
6954 }
6955 chk->index = 1;
6956 LIST_ADDQ(&rs->rules, &chk->list);
6957
6958 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006959 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006960 if (!chk) {
6961 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6962 goto error;
6963 }
6964 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6965 chk->index = 2;
6966 LIST_ADDQ(&rs->rules, &chk->list);
6967
Christopher Faulet1997eca2020-04-03 23:13:50 +02006968 ruleset_found:
6969 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006970 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006971
6972 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006973 free(errmsg);
6974 return err_code;
6975
6976 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006977 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006978 err_code |= ERR_ALERT | ERR_FATAL;
6979 goto out;
6980}
6981
6982int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6983 const char *file, int line)
6984{
6985 struct tcpcheck_ruleset *rs = NULL;
6986 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6987 struct tcpcheck_rule *chk;
6988 char *spop_req = NULL;
6989 char *errmsg = NULL;
6990 int spop_len = 0, err_code = 0;
6991
6992 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6993 err_code |= ERR_WARN;
6994
6995 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6996 goto out;
6997
Christopher Faulet267b01b2020-04-04 10:27:09 +02006998 curpx->options2 &= ~PR_O2_CHK_ANY;
6999 curpx->options2 |= PR_O2_TCPCHK_CHK;
7000
7001 free_tcpcheck_vars(&rules->preset_vars);
7002 rules->list = NULL;
7003 rules->flags = 0;
7004
7005
Christopher Faulet61cc8522020-04-20 14:54:42 +02007006 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007007 if (rs)
7008 goto ruleset_found;
7009
Christopher Faulet61cc8522020-04-20 14:54:42 +02007010 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007011 if (rs == NULL) {
7012 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7013 goto error;
7014 }
7015
7016 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7017 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7018 goto error;
7019 }
7020 chunk_reset(&trash);
7021 dump_binary(&trash, spop_req, spop_len);
7022 trash.area[trash.data] = '\0';
7023
7024 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7025 1, curpx, &rs->rules, file, line, &errmsg);
7026 if (!chk) {
7027 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7028 goto error;
7029 }
7030 chk->index = 0;
7031 LIST_ADDQ(&rs->rules, &chk->list);
7032
7033 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007034 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007035 if (!chk) {
7036 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7037 goto error;
7038 }
7039 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7040 chk->index = 1;
7041 LIST_ADDQ(&rs->rules, &chk->list);
7042
Christopher Faulet267b01b2020-04-04 10:27:09 +02007043 ruleset_found:
7044 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007045 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007046
7047 out:
7048 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007049 free(errmsg);
7050 return err_code;
7051
7052 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007053 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007054 err_code |= ERR_ALERT | ERR_FATAL;
7055 goto out;
7056}
Christopher Fauletce355072020-04-02 11:44:39 +02007057
Christopher Faulete5870d82020-04-15 11:32:03 +02007058
7059struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7060{
7061 struct tcpcheck_rule *chk = NULL;
7062 struct tcpcheck_http_hdr *hdr = NULL;
7063 char *meth = NULL, *uri = NULL, *vsn = NULL;
7064 char *hdrs, *body;
7065
7066 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7067 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7068 if (hdrs == body)
7069 hdrs = NULL;
7070 if (hdrs) {
7071 *hdrs = '\0';
7072 hdrs +=2;
7073 }
7074 if (body) {
7075 *body = '\0';
7076 body += 4;
7077 }
7078 if (hdrs || body) {
7079 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7080 " Please, consider to use 'http-check send' directive instead.");
7081 }
7082
7083 chk = calloc(1, sizeof(*chk));
7084 if (!chk) {
7085 memprintf(errmsg, "out of memory");
7086 goto error;
7087 }
7088 chk->action = TCPCHK_ACT_SEND;
7089 chk->send.type = TCPCHK_SEND_HTTP;
7090 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7091 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7092 LIST_INIT(&chk->send.http.hdrs);
7093
7094 /* Copy the method, uri and version */
7095 if (*args[cur_arg]) {
7096 if (!*args[cur_arg+1])
7097 uri = args[cur_arg];
7098 else
7099 meth = args[cur_arg];
7100 }
7101 if (*args[cur_arg+1])
7102 uri = args[cur_arg+1];
7103 if (*args[cur_arg+2])
7104 vsn = args[cur_arg+2];
7105
7106 if (meth) {
7107 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7108 chk->send.http.meth.str.area = strdup(meth);
7109 chk->send.http.meth.str.data = strlen(meth);
7110 if (!chk->send.http.meth.str.area) {
7111 memprintf(errmsg, "out of memory");
7112 goto error;
7113 }
7114 }
7115 if (uri) {
7116 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007117 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007118 memprintf(errmsg, "out of memory");
7119 goto error;
7120 }
7121 }
7122 if (vsn) {
7123 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007124 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007125 memprintf(errmsg, "out of memory");
7126 goto error;
7127 }
7128 }
7129
7130 /* Copy the header */
7131 if (hdrs) {
7132 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7133 struct h1m h1m;
7134 int i, ret;
7135
7136 /* Build and parse the request */
7137 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7138
7139 h1m.flags = H1_MF_HDRS_ONLY;
7140 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7141 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7142 &h1m, NULL);
7143 if (ret <= 0) {
7144 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7145 goto error;
7146 }
7147
Christopher Fauletb61caf42020-04-21 10:57:42 +02007148 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007149 hdr = calloc(1, sizeof(*hdr));
7150 if (!hdr) {
7151 memprintf(errmsg, "out of memory");
7152 goto error;
7153 }
7154 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007155 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007156 if (!hdr->name.ptr) {
7157 memprintf(errmsg, "out of memory");
7158 goto error;
7159 }
7160
Christopher Fauletb61caf42020-04-21 10:57:42 +02007161 ist0(tmp_hdrs[i].v);
7162 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 +02007163 goto error;
7164 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7165 }
7166 }
7167
7168 /* Copy the body */
7169 if (body) {
7170 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007171 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007172 memprintf(errmsg, "out of memory");
7173 goto error;
7174 }
7175 }
7176
7177 return chk;
7178
7179 error:
7180 free_tcpcheck_http_hdr(hdr);
7181 free_tcpcheck(chk, 0);
7182 return NULL;
7183}
7184
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007185int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7186 const char *file, int line)
7187{
Christopher Faulete5870d82020-04-15 11:32:03 +02007188 struct tcpcheck_ruleset *rs = NULL;
7189 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7190 struct tcpcheck_rule *chk;
7191 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007192 int err_code = 0;
7193
7194 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7195 err_code |= ERR_WARN;
7196
7197 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7198 goto out;
7199
Christopher Faulete5870d82020-04-15 11:32:03 +02007200 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7201 if (!chk) {
7202 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7203 goto error;
7204 }
7205 if (errmsg) {
7206 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7207 err_code |= ERR_WARN;
7208 free(errmsg);
7209 errmsg = NULL;
7210 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007211
Christopher Faulete5870d82020-04-15 11:32:03 +02007212 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007213 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007214 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007215
Christopher Faulete5870d82020-04-15 11:32:03 +02007216 free_tcpcheck_vars(&rules->preset_vars);
7217 rules->list = NULL;
7218 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007219
Christopher Faulete5870d82020-04-15 11:32:03 +02007220 /* Deduce the ruleset name from the proxy info */
7221 chunk_printf(&trash, "*http-check-%s_%s-%d",
7222 ((curpx == defpx) ? "defaults" : curpx->id),
7223 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007224
Christopher Faulet61cc8522020-04-20 14:54:42 +02007225 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007226 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007227 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007228 if (rs == NULL) {
7229 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7230 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007231 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007232 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007233
Christopher Faulete5870d82020-04-15 11:32:03 +02007234 rules->list = &rs->rules;
7235 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7236 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7237 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7238 rules->list = NULL;
7239 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007240 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007241
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007242 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007243 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007244 return err_code;
7245
7246 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007247 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007248 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007249 err_code |= ERR_ALERT | ERR_FATAL;
7250 goto out;
7251}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007252
Christopher Faulet6f557912020-04-09 15:58:50 +02007253int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7254 const char *file, int line)
7255{
7256 int err_code = 0;
7257
Christopher Faulet6f557912020-04-09 15:58:50 +02007258 curpx->options2 &= ~PR_O2_CHK_ANY;
7259 curpx->options2 |= PR_O2_EXT_CHK;
7260 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7261 goto out;
7262
7263 out:
7264 return err_code;
7265}
7266
Christopher Fauletce8111e2020-04-06 15:04:11 +02007267/* Parse the "addr" server keyword */
7268static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7269 char **errmsg)
7270{
7271 struct sockaddr_storage *sk;
7272 struct protocol *proto;
7273 int port1, port2, err_code = 0;
7274
7275
7276 if (!*args[*cur_arg+1]) {
7277 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7278 goto error;
7279 }
7280
7281 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7282 if (!sk) {
7283 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7284 goto error;
7285 }
7286
7287 proto = protocol_by_family(sk->ss_family);
7288 if (!proto || !proto->connect) {
7289 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7290 args[*cur_arg], args[*cur_arg+1]);
7291 goto error;
7292 }
7293
7294 if (port1 != port2) {
7295 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7296 args[*cur_arg], args[*cur_arg+1]);
7297 goto error;
7298 }
7299
7300 srv->check.addr = srv->agent.addr = *sk;
7301 srv->flags |= SRV_F_CHECKADDR;
7302 srv->flags |= SRV_F_AGENTADDR;
7303
7304 out:
7305 return err_code;
7306
7307 error:
7308 err_code |= ERR_ALERT | ERR_FATAL;
7309 goto out;
7310}
7311
7312
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007313/* Parse the "agent-addr" server keyword */
7314static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7315 char **errmsg)
7316{
7317 int err_code = 0;
7318
7319 if (!*(args[*cur_arg+1])) {
7320 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7321 goto error;
7322 }
7323 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7324 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7325 goto error;
7326 }
7327
7328 out:
7329 return err_code;
7330
7331 error:
7332 err_code |= ERR_ALERT | ERR_FATAL;
7333 goto out;
7334}
7335
7336/* Parse the "agent-check" server keyword */
7337static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7338 char **errmsg)
7339{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007340 struct tcpcheck_ruleset *rs = NULL;
7341 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7342 struct tcpcheck_rule *chk;
7343 int err_code = 0;
7344
7345 if (srv->do_agent)
7346 goto out;
7347
7348 if (!rules) {
7349 rules = calloc(1, sizeof(*rules));
7350 if (!rules) {
7351 memprintf(errmsg, "out of memory.");
7352 goto error;
7353 }
7354 LIST_INIT(&rules->preset_vars);
7355 srv->agent.tcpcheck_rules = rules;
7356 }
7357 rules->list = NULL;
7358 rules->flags = 0;
7359
Christopher Faulet61cc8522020-04-20 14:54:42 +02007360 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007361 if (rs)
7362 goto ruleset_found;
7363
Christopher Faulet61cc8522020-04-20 14:54:42 +02007364 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007365 if (rs == NULL) {
7366 memprintf(errmsg, "out of memory.");
7367 goto error;
7368 }
7369
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007370 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007371 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7372 if (!chk) {
7373 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7374 goto error;
7375 }
7376 chk->index = 0;
7377 LIST_ADDQ(&rs->rules, &chk->list);
7378
7379 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007380 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7381 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007382 if (!chk) {
7383 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7384 goto error;
7385 }
7386 chk->expect.custom = tcpcheck_agent_expect_reply;
7387 chk->index = 1;
7388 LIST_ADDQ(&rs->rules, &chk->list);
7389
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007390 ruleset_found:
7391 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007392 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007393 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007394
7395 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007396 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007397
7398 error:
7399 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007400 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007401 err_code |= ERR_ALERT | ERR_FATAL;
7402 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007403}
7404
7405/* Parse the "agent-inter" server keyword */
7406static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7407 char **errmsg)
7408{
7409 const char *err = NULL;
7410 unsigned int delay;
7411 int err_code = 0;
7412
7413 if (!*(args[*cur_arg+1])) {
7414 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7415 goto error;
7416 }
7417
7418 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7419 if (err == PARSE_TIME_OVER) {
7420 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7421 args[*cur_arg+1], args[*cur_arg], srv->id);
7422 goto error;
7423 }
7424 else if (err == PARSE_TIME_UNDER) {
7425 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7426 args[*cur_arg+1], args[*cur_arg], srv->id);
7427 goto error;
7428 }
7429 else if (err) {
7430 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7431 *err, srv->id);
7432 goto error;
7433 }
7434 if (delay <= 0) {
7435 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7436 delay, args[*cur_arg], srv->id);
7437 goto error;
7438 }
7439 srv->agent.inter = delay;
7440
7441 out:
7442 return err_code;
7443
7444 error:
7445 err_code |= ERR_ALERT | ERR_FATAL;
7446 goto out;
7447}
7448
7449/* Parse the "agent-port" server keyword */
7450static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7451 char **errmsg)
7452{
7453 int err_code = 0;
7454
7455 if (!*(args[*cur_arg+1])) {
7456 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7457 goto error;
7458 }
7459
7460 global.maxsock++;
7461 srv->agent.port = atol(args[*cur_arg+1]);
7462
7463 out:
7464 return err_code;
7465
7466 error:
7467 err_code |= ERR_ALERT | ERR_FATAL;
7468 goto out;
7469}
7470
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007471int set_srv_agent_send(struct server *srv, const char *send)
7472{
7473 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7474 struct tcpcheck_var *var = NULL;
7475 char *str;
7476
7477 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007478 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007479 if (str == NULL || var == NULL)
7480 goto error;
7481
7482 free_tcpcheck_vars(&rules->preset_vars);
7483
7484 var->data.type = SMP_T_STR;
7485 var->data.u.str.area = str;
7486 var->data.u.str.data = strlen(str);
7487 LIST_INIT(&var->list);
7488 LIST_ADDQ(&rules->preset_vars, &var->list);
7489
7490 return 1;
7491
7492 error:
7493 free(str);
7494 free(var);
7495 return 0;
7496}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007497
7498/* Parse the "agent-send" server keyword */
7499static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7500 char **errmsg)
7501{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007502 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007503 int err_code = 0;
7504
7505 if (!*(args[*cur_arg+1])) {
7506 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7507 goto error;
7508 }
7509
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007510 if (!rules) {
7511 rules = calloc(1, sizeof(*rules));
7512 if (!rules) {
7513 memprintf(errmsg, "out of memory.");
7514 goto error;
7515 }
7516 LIST_INIT(&rules->preset_vars);
7517 srv->agent.tcpcheck_rules = rules;
7518 }
7519
7520 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007521 memprintf(errmsg, "out of memory.");
7522 goto error;
7523 }
7524
7525 out:
7526 return err_code;
7527
7528 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007529 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007530 err_code |= ERR_ALERT | ERR_FATAL;
7531 goto out;
7532}
7533
7534/* Parse the "no-agent-send" server keyword */
7535static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7536 char **errmsg)
7537{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007538 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007539 return 0;
7540}
7541
Christopher Fauletce8111e2020-04-06 15:04:11 +02007542/* Parse the "check" server keyword */
7543static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7544 char **errmsg)
7545{
7546 srv->do_check = 1;
7547 return 0;
7548}
7549
7550/* Parse the "check-send-proxy" server keyword */
7551static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7552 char **errmsg)
7553{
7554 srv->check.send_proxy = 1;
7555 return 0;
7556}
7557
7558/* Parse the "check-via-socks4" server keyword */
7559static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7560 char **errmsg)
7561{
7562 srv->check.via_socks4 = 1;
7563 return 0;
7564}
7565
7566/* Parse the "no-check" server keyword */
7567static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7568 char **errmsg)
7569{
7570 deinit_srv_check(srv);
7571 return 0;
7572}
7573
7574/* Parse the "no-check-send-proxy" server keyword */
7575static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7576 char **errmsg)
7577{
7578 srv->check.send_proxy = 0;
7579 return 0;
7580}
7581
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007582/* parse the "check-proto" server keyword */
7583static int srv_parse_check_proto(char **args, int *cur_arg,
7584 struct proxy *px, struct server *newsrv, char **err)
7585{
7586 int err_code = 0;
7587
7588 if (!*args[*cur_arg + 1]) {
7589 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7590 goto error;
7591 }
7592 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7593 if (!newsrv->check.mux_proto) {
7594 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7595 goto error;
7596 }
7597
7598 out:
7599 return err_code;
7600
7601 error:
7602 err_code |= ERR_ALERT | ERR_FATAL;
7603 goto out;
7604}
7605
7606
Christopher Fauletce8111e2020-04-06 15:04:11 +02007607/* Parse the "rise" server keyword */
7608static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7609 char **errmsg)
7610{
7611 int err_code = 0;
7612
7613 if (!*args[*cur_arg + 1]) {
7614 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7615 goto error;
7616 }
7617
7618 srv->check.rise = atol(args[*cur_arg+1]);
7619 if (srv->check.rise <= 0) {
7620 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7621 goto error;
7622 }
7623
7624 if (srv->check.health)
7625 srv->check.health = srv->check.rise;
7626
7627 out:
7628 return err_code;
7629
7630 error:
7631 deinit_srv_agent_check(srv);
7632 err_code |= ERR_ALERT | ERR_FATAL;
7633 goto out;
7634 return 0;
7635}
7636
7637/* Parse the "fall" server keyword */
7638static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7639 char **errmsg)
7640{
7641 int err_code = 0;
7642
7643 if (!*args[*cur_arg + 1]) {
7644 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7645 goto error;
7646 }
7647
7648 srv->check.fall = atol(args[*cur_arg+1]);
7649 if (srv->check.fall <= 0) {
7650 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7651 goto error;
7652 }
7653
7654 out:
7655 return err_code;
7656
7657 error:
7658 deinit_srv_agent_check(srv);
7659 err_code |= ERR_ALERT | ERR_FATAL;
7660 goto out;
7661 return 0;
7662}
7663
7664/* Parse the "inter" server keyword */
7665static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7666 char **errmsg)
7667{
7668 const char *err = NULL;
7669 unsigned int delay;
7670 int err_code = 0;
7671
7672 if (!*(args[*cur_arg+1])) {
7673 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7674 goto error;
7675 }
7676
7677 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7678 if (err == PARSE_TIME_OVER) {
7679 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7680 args[*cur_arg+1], args[*cur_arg], srv->id);
7681 goto error;
7682 }
7683 else if (err == PARSE_TIME_UNDER) {
7684 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7685 args[*cur_arg+1], args[*cur_arg], srv->id);
7686 goto error;
7687 }
7688 else if (err) {
7689 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7690 *err, srv->id);
7691 goto error;
7692 }
7693 if (delay <= 0) {
7694 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7695 delay, args[*cur_arg], srv->id);
7696 goto error;
7697 }
7698 srv->check.inter = delay;
7699
7700 out:
7701 return err_code;
7702
7703 error:
7704 err_code |= ERR_ALERT | ERR_FATAL;
7705 goto out;
7706}
7707
7708
7709/* Parse the "fastinter" server keyword */
7710static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7711 char **errmsg)
7712{
7713 const char *err = NULL;
7714 unsigned int delay;
7715 int err_code = 0;
7716
7717 if (!*(args[*cur_arg+1])) {
7718 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7719 goto error;
7720 }
7721
7722 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7723 if (err == PARSE_TIME_OVER) {
7724 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7725 args[*cur_arg+1], args[*cur_arg], srv->id);
7726 goto error;
7727 }
7728 else if (err == PARSE_TIME_UNDER) {
7729 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7730 args[*cur_arg+1], args[*cur_arg], srv->id);
7731 goto error;
7732 }
7733 else if (err) {
7734 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7735 *err, srv->id);
7736 goto error;
7737 }
7738 if (delay <= 0) {
7739 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7740 delay, args[*cur_arg], srv->id);
7741 goto error;
7742 }
7743 srv->check.fastinter = delay;
7744
7745 out:
7746 return err_code;
7747
7748 error:
7749 err_code |= ERR_ALERT | ERR_FATAL;
7750 goto out;
7751}
7752
7753
7754/* Parse the "downinter" server keyword */
7755static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7756 char **errmsg)
7757{
7758 const char *err = NULL;
7759 unsigned int delay;
7760 int err_code = 0;
7761
7762 if (!*(args[*cur_arg+1])) {
7763 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7764 goto error;
7765 }
7766
7767 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7768 if (err == PARSE_TIME_OVER) {
7769 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7770 args[*cur_arg+1], args[*cur_arg], srv->id);
7771 goto error;
7772 }
7773 else if (err == PARSE_TIME_UNDER) {
7774 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7775 args[*cur_arg+1], args[*cur_arg], srv->id);
7776 goto error;
7777 }
7778 else if (err) {
7779 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7780 *err, srv->id);
7781 goto error;
7782 }
7783 if (delay <= 0) {
7784 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7785 delay, args[*cur_arg], srv->id);
7786 goto error;
7787 }
7788 srv->check.downinter = delay;
7789
7790 out:
7791 return err_code;
7792
7793 error:
7794 err_code |= ERR_ALERT | ERR_FATAL;
7795 goto out;
7796}
7797
7798/* Parse the "port" server keyword */
7799static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7800 char **errmsg)
7801{
7802 int err_code = 0;
7803
7804 if (!*(args[*cur_arg+1])) {
7805 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7806 goto error;
7807 }
7808
7809 global.maxsock++;
7810 srv->check.port = atol(args[*cur_arg+1]);
7811 srv->flags |= SRV_F_CHECKPORT;
7812
7813 out:
7814 return err_code;
7815
7816 error:
7817 err_code |= ERR_ALERT | ERR_FATAL;
7818 goto out;
7819}
7820
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007821static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007822 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7823 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7824 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007825 { 0, NULL, NULL },
7826}};
7827
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007828static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007829 { "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 +02007830 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7831 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7832 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7833 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7834 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007835 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007836 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007837 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7838 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007839 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007840 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7841 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7842 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7843 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7844 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7845 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7846 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7847 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007848 { NULL, NULL, 0 },
7849}};
7850
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007851INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007852INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007853
Willy Tarreaubd741542010-03-16 18:46:54 +01007854/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007855 * Local variables:
7856 * c-indent-level: 8
7857 * c-basic-offset: 8
7858 * End:
7859 */