blob: 2ffa1947b0a6ab55a3724221163aa0950306f462 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Willy Tarreau9b39dc52014-07-08 00:54:10 +020018#include <signal.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090019#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020020#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020021#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020023#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020024#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010025#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040027#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090028#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020030#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031#include <arpa/inet.h>
32
Christopher Fauletfd6c2292020-03-25 18:20:15 +010033#include <common/cfgparse.h>
Willy Tarreauc7e42382012-08-24 19:22:53 +020034#include <common/chunk.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020035#include <common/compat.h>
36#include <common/config.h>
37#include <common/mini-clist.h>
Willy Tarreau83749182007-04-15 20:56:27 +020038#include <common/standard.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020039#include <common/time.h>
Christopher Fauletcfda8472017-10-20 15:40:23 +020040#include <common/hathreads.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020041
42#include <types/global.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020043#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010044#include <types/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020045
Gaetan Rivet707b52f2020-02-21 18:14:59 +010046#include <proto/action.h>
Christopher Fauletba3c68f2020-04-01 16:27:05 +020047#include <proto/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020048#include <proto/backend.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020049#include <proto/checks.h>
William Lallemand9ed62032016-11-21 17:49:11 +010050#include <proto/stats.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020051#include <proto/fd.h>
52#include <proto/log.h>
Willy Tarreau53a47662017-08-28 10:53:00 +020053#include <proto/mux_pt.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020054#include <proto/queue.h>
Willy Tarreauc6f4ce82009-06-10 11:09:37 +020055#include <proto/port_range.h>
Willy Tarreaue8c66af2008-01-13 18:40:14 +010056#include <proto/proto_tcp.h>
Baptiste Assmann69e273f2013-12-11 00:52:19 +010057#include <proto/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010058#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020059#include <proto/server.h>
Willy Tarreau48d6bf22016-06-21 16:27:34 +020060#include <proto/signal.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010061#include <proto/stream_interface.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020062#include <proto/task.h>
Gaetan Rivet13a50432020-02-21 18:13:44 +010063#include <proto/vars.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020064#include <proto/log.h>
65#include <proto/dns.h>
66#include <proto/proto_udp.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020067#include <proto/ssl_sock.h>
Christopher Fauletb7d30092020-03-30 15:19:03 +020068#include <proto/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020069
Willy Tarreaubd741542010-03-16 18:46:54 +010070static int httpchk_expect(struct server *s, int done);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020071static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Willy Tarreau6bdcab02017-10-04 18:41:00 +020072static int tcpcheck_main(struct check *);
Olivier Houchard910b2bc2018-07-17 18:49:38 +020073static void __event_srv_chk_w(struct conn_stream *cs);
Olivier Houchard4501c3e2018-08-28 19:36:18 +020074static int wake_srv_chk(struct conn_stream *cs);
Olivier Houchardaf4021e2018-08-09 13:06:55 +020075static void __event_srv_chk_r(struct conn_stream *cs);
Willy Tarreaubd741542010-03-16 18:46:54 +010076
Christopher Faulet31c30fd2020-03-26 21:10:03 +010077static int srv_check_healthcheck_port(struct check *chk);
78
Christopher Faulet5d503fc2020-03-30 20:34:34 +020079/* Global list to share all tcp-checks */
80struct list tcpchecks_list = LIST_HEAD_INIT(tcpchecks_list);
81
82
Willy Tarreau8ceae722018-11-26 11:58:30 +010083DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
84DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020085
Gaetan Rivet05d692d2020-02-14 17:42:54 +010086/* Dummy frontend used to create all checks sessions. */
87static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020088
Simon Horman63a4a822012-03-19 07:24:41 +090089static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010090 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
91 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +020092 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020093
Willy Tarreau23964182014-05-20 20:56:30 +020094 /* Below we have finished checks */
95 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010096 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +010097
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010098 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020099
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100100 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
101 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
102 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200103
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100104 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
105 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
106 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200107
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100108 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
109 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200110
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200111 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200112
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
114 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
115 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900116
117 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
118 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200119 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200120};
121
Cyril Bontéac92a062014-12-27 22:28:38 +0100122const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
123 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
124 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
125 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
126 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
127 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
128 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
129 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200130 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
131 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
Cyril Bontéac92a062014-12-27 22:28:38 +0100132 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
133 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
134};
135
Simon Horman63a4a822012-03-19 07:24:41 +0900136static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100137 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
138
139 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
140 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
141
142 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
143 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
144 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
145 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
146
147 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
148 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
149 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
150};
151
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100152/* checks if <err> is a real error for errno or one that can be ignored, and
153 * return 0 for these ones or <err> for real ones.
154 */
155static inline int unclean_errno(int err)
156{
157 if (err == EAGAIN || err == EINPROGRESS ||
158 err == EISCONN || err == EALREADY)
159 return 0;
160 return err;
161}
162
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200163/*
164 * Convert check_status code to description
165 */
166const char *get_check_status_description(short check_status) {
167
168 const char *desc;
169
170 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200171 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200172 else
173 desc = NULL;
174
175 if (desc && *desc)
176 return desc;
177 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200178 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200179}
180
181/*
182 * Convert check_status code to short info
183 */
184const 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
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100199const char *get_analyze_status(short analyze_status) {
200
201 const char *desc;
202
203 if (analyze_status < HANA_STATUS_SIZE)
204 desc = analyze_statuses[analyze_status].desc;
205 else
206 desc = NULL;
207
208 if (desc && *desc)
209 return desc;
210 else
211 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
212}
213
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200214/*
Simon Horman4a741432013-02-23 15:35:38 +0900215 * Set check->status, update check->duration and fill check->result with
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200216 * an adequate CHK_RES_* value. The new check->health is computed based
217 * on the result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200218 *
219 * Show information in logs about failed health check if server is UP
220 * or 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 }
494}
495
Willy Tarreaua1dab552014-04-14 15:04:54 +0200496static int httpchk_build_status_header(struct server *s, char *buffer, int size)
Willy Tarreauef781042010-01-27 11:53:01 +0100497{
498 int sv_state;
499 int ratio;
500 int hlen = 0;
Joseph Lynch514061c2015-01-15 17:52:59 -0800501 char addr[46];
502 char port[6];
Willy Tarreauef781042010-01-27 11:53:01 +0100503 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
504 "UP %d/%d", "UP",
505 "NOLB %d/%d", "NOLB",
506 "no check" };
507
508 memcpy(buffer + hlen, "X-Haproxy-Server-State: ", 24);
509 hlen += 24;
510
Willy Tarreauff5ae352013-12-11 20:36:34 +0100511 if (!(s->check.state & CHK_ST_ENABLED))
512 sv_state = 6;
Emeric Brun52a91d32017-08-31 14:41:55 +0200513 else if (s->cur_state != SRV_ST_STOPPED) {
Simon Horman58c32972013-11-25 10:46:38 +0900514 if (s->check.health == s->check.rise + s->check.fall - 1)
Willy Tarreauef781042010-01-27 11:53:01 +0100515 sv_state = 3; /* UP */
516 else
517 sv_state = 2; /* going down */
518
Emeric Brun52a91d32017-08-31 14:41:55 +0200519 if (s->cur_state == SRV_ST_STOPPING)
Willy Tarreauef781042010-01-27 11:53:01 +0100520 sv_state += 2;
521 } else {
Simon Horman125d0992013-02-24 17:23:38 +0900522 if (s->check.health)
Willy Tarreauef781042010-01-27 11:53:01 +0100523 sv_state = 1; /* going up */
524 else
525 sv_state = 0; /* DOWN */
526 }
527
Willy Tarreaua1dab552014-04-14 15:04:54 +0200528 hlen += snprintf(buffer + hlen, size - hlen,
Willy Tarreauef781042010-01-27 11:53:01 +0100529 srv_hlt_st[sv_state],
Emeric Brun52a91d32017-08-31 14:41:55 +0200530 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
531 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreauef781042010-01-27 11:53:01 +0100532
Joseph Lynch514061c2015-01-15 17:52:59 -0800533 addr_to_str(&s->addr, addr, sizeof(addr));
Willy Tarreau04276f32017-01-06 17:41:29 +0100534 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
535 snprintf(port, sizeof(port), "%u", s->svc_port);
536 else
537 *port = 0;
Joseph Lynch514061c2015-01-15 17:52:59 -0800538
539 hlen += snprintf(buffer + hlen, size - hlen, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
540 addr, port, s->proxy->id, s->id,
Willy Tarreauef781042010-01-27 11:53:01 +0100541 global.node,
Emeric Brun52a91d32017-08-31 14:41:55 +0200542 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
Willy Tarreauef781042010-01-27 11:53:01 +0100543 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
544 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
545 s->nbpend);
546
Emeric Brun52a91d32017-08-31 14:41:55 +0200547 if ((s->cur_state == SRV_ST_STARTING) &&
Willy Tarreauef781042010-01-27 11:53:01 +0100548 now.tv_sec < s->last_change + s->slowstart &&
549 now.tv_sec >= s->last_change) {
550 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
Willy Tarreaua1dab552014-04-14 15:04:54 +0200551 hlen += snprintf(buffer + hlen, size - hlen, "; throttle=%d%%", ratio);
Willy Tarreauef781042010-01-27 11:53:01 +0100552 }
553
554 buffer[hlen++] = '\r';
555 buffer[hlen++] = '\n';
556
557 return hlen;
558}
559
Willy Tarreau20a18342013-12-05 00:31:46 +0100560/* Check the connection. If an error has already been reported or the socket is
561 * closed, keep errno intact as it is supposed to contain the valid error code.
562 * If no error is reported, check the socket's error queue using getsockopt().
563 * Warning, this must be done only once when returning from poll, and never
564 * after an I/O error was attempted, otherwise the error queue might contain
565 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
566 * socket. Returns non-zero if an error was reported, zero if everything is
567 * clean (including a properly closed socket).
568 */
569static int retrieve_errno_from_socket(struct connection *conn)
570{
571 int skerr;
572 socklen_t lskerr = sizeof(skerr);
573
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100574 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100575 return 1;
576
Willy Tarreau3c728722014-01-23 13:50:42 +0100577 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100578 return 0;
579
Willy Tarreau585744b2017-08-24 14:31:19 +0200580 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100581 errno = skerr;
582
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100583 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100584
585 if (!errno) {
586 /* we could not retrieve an error, that does not mean there is
587 * none. Just don't change anything and only report the prior
588 * error if any.
589 */
590 if (conn->flags & CO_FL_ERROR)
591 return 1;
592 else
593 return 0;
594 }
595
596 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
597 return 1;
598}
599
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100600/* Try to collect as much information as possible on the connection status,
601 * and adjust the server status accordingly. It may make use of <errno_bck>
602 * if non-null when the caller is absolutely certain of its validity (eg:
603 * checked just after a syscall). If the caller doesn't have a valid errno,
604 * it can pass zero, and retrieve_errno_from_socket() will be called to try
605 * to extract errno from the socket. If no error is reported, it will consider
606 * the <expired> flag. This is intended to be used when a connection error was
607 * reported in conn->flags or when a timeout was reported in <expired>. The
608 * function takes care of not updating a server status which was already set.
609 * All situations where at least one of <expired> or CO_FL_ERROR are set
610 * produce a status.
611 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200612static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100613{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200614 struct conn_stream *cs = check->cs;
615 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100616 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200617 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200618 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100619
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100620 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100621 return;
622
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100623 errno = unclean_errno(errno_bck);
624 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100625 retrieve_errno_from_socket(conn);
626
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200627 if (conn && !(conn->flags & CO_FL_ERROR) &&
628 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100629 return;
630
631 /* we'll try to build a meaningful error message depending on the
632 * context of the error possibly present in conn->err_code, and the
633 * socket error possibly collected above. This is useful to know the
634 * exact step of the L6 layer (eg: SSL handshake).
635 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200636 chk = get_trash_chunk();
637
638 if (check->type == PR_O2_TCPCHK_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200639 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200640 if (!step)
641 chunk_printf(chk, " at initial connection step of tcp-check");
642 else {
643 chunk_printf(chk, " at step %d of tcp-check", step);
644 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200645 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
646 if (check->current_step->connect.port)
647 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200648 else
649 chunk_appendf(chk, " (connect)");
650 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200651 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
652 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100653
654 switch (expect->type) {
655 case TCPCHK_EXPECT_STRING:
656 chunk_appendf(chk, " (expect string '%s')", expect->string);
657 break;
658 case TCPCHK_EXPECT_BINARY:
659 chunk_appendf(chk, " (expect binary '%s')", expect->string);
660 break;
661 case TCPCHK_EXPECT_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200662 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100663 break;
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100664 case TCPCHK_EXPECT_REGEX_BINARY:
665 chunk_appendf(chk, " (expect binary regex)");
666 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200667 case TCPCHK_EXPECT_CUSTOM:
668 chunk_appendf(chk, " (expect custom function)");
669 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100670 case TCPCHK_EXPECT_UNDEF:
671 chunk_appendf(chk, " (undefined expect!)");
672 break;
673 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200674 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200675 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200676 chunk_appendf(chk, " (send)");
677 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200678
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200679 if (check->current_step && check->current_step->comment)
680 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200681 }
682 }
683
Willy Tarreau00149122017-10-04 18:05:01 +0200684 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100685 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200686 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
687 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100688 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200689 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
690 chk->area);
691 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100692 }
693 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100694 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200695 chunk_printf(&trash, "%s%s", strerror(errno),
696 chk->area);
697 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100698 }
699 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200700 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100701 }
702 }
703
Willy Tarreau00149122017-10-04 18:05:01 +0200704 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200705 /* NOTE: this is reported after <fall> tries */
706 chunk_printf(chk, "No port available for the TCP connection");
707 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
708 }
709
Willy Tarreau00149122017-10-04 18:05:01 +0200710 if (!conn) {
711 /* connection allocation error before the connection was established */
712 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
713 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100714 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100715 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200716 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100717 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
718 else if (expired)
719 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200720
721 /*
722 * might be due to a server IP change.
723 * Let's trigger a DNS resolution if none are currently running.
724 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100725 if (check->server)
726 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200727
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100728 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100729 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100730 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200731 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100732 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
733 else if (expired)
734 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
735 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200736 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100737 /* I/O error after connection was established and before we could diagnose */
738 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
739 }
740 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200741 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
742
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100743 /* connection established but expired check */
Christopher Faulet811f78c2020-04-01 11:10:27 +0200744 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT)
745 tout = check->current_step->expect.tout_status;
746 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100747 }
748
749 return;
750}
751
Olivier Houchard5c110b92018-08-14 17:04:58 +0200752/* This function checks if any I/O is wanted, and if so, attempts to do so */
753static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200754{
Olivier Houchard26e1a8f2018-09-12 15:15:12 +0200755 struct check *check = ctx;
756 struct conn_stream *cs = check->cs;
Olivier Houchard0923fa42019-01-11 18:43:04 +0100757 struct email_alertq *q = container_of(check, typeof(*q), check);
Olivier Houchardbc89ad82019-07-09 17:28:51 +0200758 int ret = 0;
Olivier Houchard4501c3e2018-08-28 19:36:18 +0200759
Willy Tarreau4f6516d2018-12-19 13:59:17 +0100760 if (!(check->wait_list.events & SUB_RETRY_SEND))
Olivier Houchardbc89ad82019-07-09 17:28:51 +0200761 ret = wake_srv_chk(cs);
762 if (ret == 0 && !(check->wait_list.events & SUB_RETRY_RECV)) {
Olivier Houchard0923fa42019-01-11 18:43:04 +0100763 if (check->server)
764 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
765 else
766 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Olivier Houchardaf4021e2018-08-09 13:06:55 +0200767 __event_srv_chk_r(cs);
Olivier Houchard0923fa42019-01-11 18:43:04 +0100768 if (check->server)
769 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
770 else
771 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Olivier Houchardaf4021e2018-08-09 13:06:55 +0200772 }
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200773 return NULL;
774}
775
776/* same as above but protected by the server lock.
Willy Tarreau62ac84f2017-11-05 10:11:13 +0100777 *
778 * Please do NOT place any return statement in this function and only leave
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200779 * via the out label. NOTE THAT THIS FUNCTION DOESN'T LOCK, YOU PROBABLY WANT
780 * TO USE event_srv_chk_w() instead.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200781 */
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200782static void __event_srv_chk_w(struct conn_stream *cs)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200783{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200784 struct connection *conn = cs->conn;
785 struct check *check = cs->data;
Simon Horman4a741432013-02-23 15:35:38 +0900786 struct server *s = check->server;
Simon Horman4a741432013-02-23 15:35:38 +0900787 struct task *t = check->task;
Willy Tarreaufb56aab2012-09-28 14:40:02 +0200788
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100789 if (unlikely(check->result == CHK_RES_FAILED))
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100790 goto out_wakeup;
791
Willy Tarreau20a18342013-12-05 00:31:46 +0100792 if (retrieve_errno_from_socket(conn)) {
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200793 chk_report_conn_err(check, errno, 0);
Willy Tarreau20a18342013-12-05 00:31:46 +0100794 goto out_wakeup;
795 }
Krzysztof Piotr Oledzki6492db52010-01-02 22:03:01 +0100796
Willy Tarreau06559ac2013-12-05 01:53:08 +0100797 /* here, we know that the connection is established. That's enough for
798 * a pure TCP check.
799 */
800 if (!check->type)
801 goto out_wakeup;
802
Willy Tarreauc09572f2017-10-04 11:58:22 +0200803 /* wake() will take care of calling tcpcheck_main() */
Willy Tarreau62ac84f2017-11-05 10:11:13 +0100804 if (check->type == PR_O2_TCPCHK_CHK)
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200805 goto out;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200806
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200807 if (b_data(&check->bo)) {
Olivier Houcharded0f2072018-08-16 15:41:52 +0200808 cs->conn->mux->snd_buf(cs, &check->bo, b_data(&check->bo), 0);
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200809 b_realign_if_empty(&check->bo);
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200810 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200811 chk_report_conn_err(check, errno, 0);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100812 goto out_wakeup;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200813 }
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200814 if (b_data(&check->bo)) {
Willy Tarreau4f6516d2018-12-19 13:59:17 +0100815 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200816 goto out;
817 }
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100818 }
Willy Tarreau6996e152007-04-30 14:37:43 +0200819
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100820 /* full request sent, we allow up to <timeout.check> if nonzero for a response */
821 if (s->proxy->timeout.check) {
822 t->expire = tick_add_ifset(now_ms, s->proxy->timeout.check);
823 task_queue(t);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200824 }
Olivier Houchard53216e72018-10-10 15:46:36 +0200825 goto out;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100826
Willy Tarreau83749182007-04-15 20:56:27 +0200827 out_wakeup:
Willy Tarreaufdccded2008-08-29 18:19:04 +0200828 task_wakeup(t, TASK_WOKEN_IO);
Olivier Houchard910b2bc2018-07-17 18:49:38 +0200829 out:
830 return;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200831}
832
Willy Tarreaubaaee002006-06-26 02:48:02 +0200833/*
Willy Tarreauf3c69202006-07-09 16:42:34 +0200834 * This function is used only for server health-checks. It handles the server's
Hervé COMMOWICK8776f1b2010-10-18 15:58:36 +0200835 * reply to an HTTP request, SSL HELLO or MySQL client Auth. It calls
Simon Horman4a741432013-02-23 15:35:38 +0900836 * set_server_check_status() to update check->status, check->duration
837 * and check->result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200838
839 * The set_server_check_status function is called with HCHK_STATUS_L7OKD if
840 * an HTTP server replies HTTP 2xx or 3xx (valid responses), if an SMTP server
841 * returns 2xx, HCHK_STATUS_L6OK if an SSL server returns at least 5 bytes in
842 * response to an SSL HELLO (the principle is that this is enough to
843 * distinguish between an SSL server and a pure TCP relay). All other cases will
844 * call it with a proper error status like HCHK_STATUS_L7STS, HCHK_STATUS_L6RSP,
845 * etc.
Willy Tarreau62ac84f2017-11-05 10:11:13 +0100846 *
847 * Please do NOT place any return statement in this function and only leave
Olivier Houchardaf4021e2018-08-09 13:06:55 +0200848 * via the out label.
849 *
850 * This must be called with the server lock held.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200851 */
Olivier Houchardaf4021e2018-08-09 13:06:55 +0200852static void __event_srv_chk_r(struct conn_stream *cs)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200853{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200854 struct connection *conn = cs->conn;
855 struct check *check = cs->data;
Simon Horman4a741432013-02-23 15:35:38 +0900856 struct server *s = check->server;
857 struct task *t = check->task;
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200858 char *desc;
Willy Tarreau03938182010-03-17 21:52:07 +0100859 int done;
Willy Tarreau83749182007-04-15 20:56:27 +0200860
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100861 if (unlikely(check->result == CHK_RES_FAILED))
Willy Tarreau83749182007-04-15 20:56:27 +0200862 goto out_wakeup;
Willy Tarreau83749182007-04-15 20:56:27 +0200863
Willy Tarreauc09572f2017-10-04 11:58:22 +0200864 /* wake() will take care of calling tcpcheck_main() */
Willy Tarreau62ac84f2017-11-05 10:11:13 +0100865 if (check->type == PR_O2_TCPCHK_CHK)
Olivier Houchardaf4021e2018-08-09 13:06:55 +0200866 goto out;
Willy Tarreaufb56aab2012-09-28 14:40:02 +0200867
Willy Tarreau83749182007-04-15 20:56:27 +0200868 /* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
869 * but the connection was closed on the remote end. Fortunately, recv still
870 * works correctly and we don't need to do the getsockopt() on linux.
871 */
Nick Chalk57b1bf72010-03-16 15:50:46 +0000872
873 /* Set buffer to point to the end of the data already read, and check
874 * that there is free space remaining. If the buffer is full, proceed
875 * with running the checks without attempting another socket read.
876 */
Nick Chalk57b1bf72010-03-16 15:50:46 +0000877
Willy Tarreau03938182010-03-17 21:52:07 +0100878 done = 0;
Nick Chalk57b1bf72010-03-16 15:50:46 +0000879
Olivier Houchard511efea2018-08-16 15:30:32 +0200880 cs->conn->mux->rcv_buf(cs, &check->bi, b_size(&check->bi), 0);
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200881 if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH) || cs->flags & CS_FL_ERROR) {
Willy Tarreau03938182010-03-17 21:52:07 +0100882 done = 1;
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200883 if ((conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) && !b_data(&check->bi)) {
Willy Tarreauf1503172012-09-28 19:39:36 +0200884 /* Report network errors only if we got no other data. Otherwise
885 * we'll let the upper layers decide whether the response is OK
886 * or not. It is very common that an RST sent by the server is
887 * reported as an error just after the last data chunk.
888 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200889 chk_report_conn_err(check, errno, 0);
Willy Tarreauc1a07962010-03-16 20:55:43 +0100890 goto out_wakeup;
891 }
Willy Tarreaubaaee002006-06-26 02:48:02 +0200892 }
893
Willy Tarreau0f0393f2019-09-24 10:43:03 +0200894 /* the rest of the code below expects the connection to be ready! */
Willy Tarreau911db9b2020-01-23 16:27:54 +0100895 if (conn->flags & CO_FL_WAIT_XPRT && !done)
Willy Tarreau0f0393f2019-09-24 10:43:03 +0200896 goto wait_more_data;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100897
Willy Tarreau03938182010-03-17 21:52:07 +0100898 /* Intermediate or complete response received.
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200899 * Terminate string in b_head(&check->bi) buffer.
Willy Tarreau03938182010-03-17 21:52:07 +0100900 */
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200901 if (b_data(&check->bi) < b_size(&check->bi))
902 b_head(&check->bi)[b_data(&check->bi)] = '\0';
Willy Tarreau03938182010-03-17 21:52:07 +0100903 else {
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200904 b_head(&check->bi)[b_data(&check->bi) - 1] = '\0';
Willy Tarreau03938182010-03-17 21:52:07 +0100905 done = 1; /* buffer full, don't wait for more data */
906 }
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200907
Nick Chalk57b1bf72010-03-16 15:50:46 +0000908 /* Run the checks... */
Simon Horman4a741432013-02-23 15:35:38 +0900909 switch (check->type) {
Willy Tarreau1620ec32011-08-06 17:05:02 +0200910 case PR_O2_HTTP_CHK:
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200911 if (!done && b_data(&check->bi) < strlen("HTTP/1.0 000\r"))
Willy Tarreau03938182010-03-17 21:52:07 +0100912 goto wait_more_data;
913
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100914 /* Check if the server speaks HTTP 1.X */
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200915 if ((b_data(&check->bi) < strlen("HTTP/1.0 000\r")) ||
916 (memcmp(b_head(&check->bi), "HTTP/1.", 7) != 0 ||
917 (*(b_head(&check->bi) + 12) != ' ' && *(b_head(&check->bi) + 12) != '\r')) ||
918 !isdigit((unsigned char) *(b_head(&check->bi) + 9)) || !isdigit((unsigned char) *(b_head(&check->bi) + 10)) ||
919 !isdigit((unsigned char) *(b_head(&check->bi) + 11))) {
920 cut_crlf(b_head(&check->bi));
921 set_server_check_status(check, HCHK_STATUS_L7RSP, b_head(&check->bi));
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200922
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100923 goto out_wakeup;
924 }
925
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200926 check->code = str2uic(b_head(&check->bi) + 9);
927 desc = ltrim(b_head(&check->bi) + 12, ' ');
Christopher Fauletc2a89a62017-10-23 15:54:24 +0200928
Willy Tarreaubd741542010-03-16 18:46:54 +0100929 if ((s->proxy->options & PR_O_DISABLE404) &&
Emeric Brun52a91d32017-08-31 14:41:55 +0200930 (s->next_state != SRV_ST_STOPPED) && (check->code == 404)) {
Nick Chalk57b1bf72010-03-16 15:50:46 +0000931 /* 404 may be accepted as "stopping" only if the server was up */
932 cut_crlf(desc);
Simon Horman4a741432013-02-23 15:35:38 +0900933 set_server_check_status(check, HCHK_STATUS_L7OKCD, desc);
Nick Chalk57b1bf72010-03-16 15:50:46 +0000934 }
Willy Tarreaubd741542010-03-16 18:46:54 +0100935 else if (s->proxy->options2 & PR_O2_EXP_TYPE) {
936 /* Run content verification check... We know we have at least 13 chars */
937 if (!httpchk_expect(s, done))
938 goto wait_more_data;
939 }
940 /* check the reply : HTTP/1.X 2xx and 3xx are OK */
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200941 else if (*(b_head(&check->bi) + 9) == '2' || *(b_head(&check->bi) + 9) == '3') {
Willy Tarreaubd741542010-03-16 18:46:54 +0100942 cut_crlf(desc);
Simon Horman4a741432013-02-23 15:35:38 +0900943 set_server_check_status(check, HCHK_STATUS_L7OKD, desc);
Willy Tarreaubd741542010-03-16 18:46:54 +0100944 }
Nick Chalk57b1bf72010-03-16 15:50:46 +0000945 else {
946 cut_crlf(desc);
Simon Horman4a741432013-02-23 15:35:38 +0900947 set_server_check_status(check, HCHK_STATUS_L7STS, desc);
Nick Chalk57b1bf72010-03-16 15:50:46 +0000948 }
Willy Tarreau1620ec32011-08-06 17:05:02 +0200949 break;
950
Willy Tarreau1620ec32011-08-06 17:05:02 +0200951 default:
Willy Tarreau4c1a2b32019-09-05 18:43:22 +0200952 /* good connection is enough for pure TCP check */
Willy Tarreau911db9b2020-01-23 16:27:54 +0100953 if (!(conn->flags & CO_FL_WAIT_XPRT) && !check->type) {
Christopher Fauletf61f33a2020-03-27 18:55:49 +0100954 if (check->use_ssl == 1)
Willy Tarreau4c1a2b32019-09-05 18:43:22 +0200955 set_server_check_status(check, HCHK_STATUS_L6OK, NULL);
956 else
957 set_server_check_status(check, HCHK_STATUS_L4OK, NULL);
958 }
Willy Tarreau1620ec32011-08-06 17:05:02 +0200959 break;
960 } /* switch */
Willy Tarreau83749182007-04-15 20:56:27 +0200961
Willy Tarreauc7dd71a2007-11-30 08:33:21 +0100962 out_wakeup:
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100963 /* collect possible new errors */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200964 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200965 chk_report_conn_err(check, 0, 0);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200966
Nick Chalk57b1bf72010-03-16 15:50:46 +0000967 /* Reset the check buffer... */
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200968 *b_head(&check->bi) = '\0';
969 b_reset(&check->bi);
Nick Chalk57b1bf72010-03-16 15:50:46 +0000970
Steven Davidovitz544d4812017-03-08 11:06:20 -0800971 /* Close the connection... We still attempt to nicely close if,
972 * for instance, SSL needs to send a "close notify." Later, we perform
973 * a hard close and reset the connection if some data are pending,
974 * otherwise we end up with many TIME_WAITs and eat all the source port
975 * range quickly. To avoid sending RSTs all the time, we first try to
976 * drain pending data.
Willy Tarreaufd29cc52012-11-23 09:18:20 +0100977 */
Olivier Houchard6c7e96a2019-07-02 16:35:18 +0200978 /* Call cs_shutr() first, to add the CO_FL_SOCK_RD_SH flag on the
979 * connection, to make sure cs_shutw() will not lead to a shutdown()
980 * that would provoke TIME_WAITs.
981 */
982 cs_shutr(cs, CS_SHR_DRAIN);
Willy Tarreauecdb3fe2017-10-05 15:25:48 +0200983 cs_shutw(cs, CS_SHW_NORMAL);
Willy Tarreau2b57cb82013-06-10 19:56:38 +0200984
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100985 /* OK, let's not stay here forever */
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100986 if (check->result == CHK_RES_FAILED)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100987 conn->flags |= CO_FL_ERROR;
988
Willy Tarreaufdccded2008-08-29 18:19:04 +0200989 task_wakeup(t, TASK_WOKEN_IO);
Olivier Houchardaf4021e2018-08-09 13:06:55 +0200990out:
Willy Tarreau3267d362012-08-17 23:53:56 +0200991 return;
Willy Tarreau03938182010-03-17 21:52:07 +0100992
993 wait_more_data:
Willy Tarreau4f6516d2018-12-19 13:59:17 +0100994 cs->conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Olivier Houchardaf4021e2018-08-09 13:06:55 +0200995 goto out;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200996}
997
Willy Tarreaufb56aab2012-09-28 14:40:02 +0200998/*
999 * This function is used only for server health-checks. It handles connection
1000 * status updates including errors. If necessary, it wakes the check task up.
Willy Tarreau6bdcab02017-10-04 18:41:00 +02001001 * It returns 0 on normal cases, <0 if at least one close() has happened on the
1002 * connection (eg: reconnect).
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001003 */
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001004static int wake_srv_chk(struct conn_stream *cs)
Willy Tarreau20bea422012-07-06 12:00:49 +02001005{
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001006 struct connection *conn = cs->conn;
1007 struct check *check = cs->data;
Olivier Houchard0923fa42019-01-11 18:43:04 +01001008 struct email_alertq *q = container_of(check, typeof(*q), check);
Willy Tarreau6bdcab02017-10-04 18:41:00 +02001009 int ret = 0;
Willy Tarreau20bea422012-07-06 12:00:49 +02001010
Olivier Houchard0923fa42019-01-11 18:43:04 +01001011 if (check->server)
1012 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
1013 else
1014 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Emeric Brunf6ba17d2017-11-02 14:35:27 +01001015
Willy Tarreauc09572f2017-10-04 11:58:22 +02001016 /* we may have to make progress on the TCP checks */
Willy Tarreau6bdcab02017-10-04 18:41:00 +02001017 if (check->type == PR_O2_TCPCHK_CHK) {
1018 ret = tcpcheck_main(check);
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001019 cs = check->cs;
Willy Tarreau543abd42018-09-20 11:25:12 +02001020 conn = cs->conn;
Willy Tarreauc5940392019-09-05 17:38:40 +02001021 } else {
1022 if (!(check->wait_list.events & SUB_RETRY_SEND))
1023 __event_srv_chk_w(cs);
1024 if (!(check->wait_list.events & SUB_RETRY_RECV))
1025 __event_srv_chk_r(cs);
1026 }
Willy Tarreauc09572f2017-10-04 11:58:22 +02001027
Willy Tarreau4ff3b892017-10-16 15:17:17 +02001028 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
Willy Tarreau02b0f582013-12-03 15:42:33 +01001029 /* We may get error reports bypassing the I/O handlers, typically
1030 * the case when sending a pure TCP check which fails, then the I/O
1031 * handlers above are not called. This is completely handled by the
Willy Tarreau25e2ab52013-12-04 11:17:05 +01001032 * main processing task so let's simply wake it up. If we get here,
1033 * we expect errno to still be valid.
1034 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +02001035 chk_report_conn_err(check, errno, 0);
Willy Tarreau2d351b62013-12-05 02:36:25 +01001036 task_wakeup(check->task, TASK_WOKEN_IO);
1037 }
Willy Tarreau911db9b2020-01-23 16:27:54 +01001038 else if (!(conn->flags & CO_FL_WAIT_XPRT) && !check->type) {
Willy Tarreau3be293f2014-02-05 18:31:24 +01001039 /* we may get here if only a connection probe was required : we
1040 * don't have any data to send nor anything expected in response,
1041 * so the completion of the connection establishment is enough.
1042 */
1043 task_wakeup(check->task, TASK_WOKEN_IO);
1044 }
Willy Tarreau2d351b62013-12-05 02:36:25 +01001045
Willy Tarreau6aaa1b82013-12-11 17:09:34 +01001046 if (check->result != CHK_RES_UNKNOWN) {
Christopher Faulet774c4862019-01-21 14:15:50 +01001047 /* Check complete or aborted. If connection not yet closed do it
1048 * now and wake the check task up to be sure the result is
1049 * handled ASAP. */
Willy Tarreaud85c4852015-03-13 00:40:28 +01001050 conn_sock_drain(conn);
Willy Tarreaua553ae92017-10-05 18:52:17 +02001051 cs_close(cs);
Willy Tarreau6bdcab02017-10-04 18:41:00 +02001052 ret = -1;
Olivier Houchardf4949572019-07-02 17:42:22 +02001053 /* We may have been scheduled to run, and the
1054 * I/O handler expects to have a cs, so remove
1055 * the tasklet
1056 */
1057 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
Christopher Faulet774c4862019-01-21 14:15:50 +01001058 task_wakeup(check->task, TASK_WOKEN_IO);
Willy Tarreau2d351b62013-12-05 02:36:25 +01001059 }
Willy Tarreau6bdcab02017-10-04 18:41:00 +02001060
Olivier Houchard0923fa42019-01-11 18:43:04 +01001061 if (check->server)
1062 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
1063 else
1064 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Emeric Brunf6ba17d2017-11-02 14:35:27 +01001065
Willy Tarreau6bdcab02017-10-04 18:41:00 +02001066 /* if a connection got replaced, we must absolutely prevent the connection
1067 * handler from touching its fd, and perform the FD polling updates ourselves
1068 */
1069 if (ret < 0)
1070 conn_cond_update_polling(conn);
1071
1072 return ret;
Willy Tarreau20bea422012-07-06 12:00:49 +02001073}
1074
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001075struct data_cb check_conn_cb = {
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001076 .wake = wake_srv_chk,
Willy Tarreau8e0bb0a2016-11-24 16:58:12 +01001077 .name = "CHCK",
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001078};
1079
Willy Tarreaubaaee002006-06-26 02:48:02 +02001080/*
Willy Tarreau2e993902011-10-31 11:53:20 +01001081 * updates the server's weight during a warmup stage. Once the final weight is
1082 * reached, the task automatically stops. Note that any server status change
1083 * must have updated s->last_change accordingly.
1084 */
Olivier Houchard9f6af332018-05-25 14:04:04 +02001085static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Willy Tarreau2e993902011-10-31 11:53:20 +01001086{
Olivier Houchard9f6af332018-05-25 14:04:04 +02001087 struct server *s = context;
Willy Tarreau2e993902011-10-31 11:53:20 +01001088
1089 /* by default, plan on stopping the task */
1090 t->expire = TICK_ETERNITY;
Emeric Brun52a91d32017-08-31 14:41:55 +02001091 if ((s->next_admin & SRV_ADMF_MAINT) ||
1092 (s->next_state != SRV_ST_STARTING))
Willy Tarreau2e993902011-10-31 11:53:20 +01001093 return t;
1094
Willy Tarreau4fc49a92019-05-05 06:54:22 +02001095 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
1096
Willy Tarreau892337c2014-05-13 23:41:20 +02001097 /* recalculate the weights and update the state */
Willy Tarreau3ff577e2018-08-02 11:48:52 +02001098 server_recalc_eweight(s, 1);
Willy Tarreau2e993902011-10-31 11:53:20 +01001099
1100 /* probably that we can refill this server with a bit more connections */
Willy Tarreau4aac7db2014-05-16 11:48:10 +02001101 pendconn_grab_from_px(s);
Willy Tarreau2e993902011-10-31 11:53:20 +01001102
Willy Tarreau4fc49a92019-05-05 06:54:22 +02001103 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
1104
Willy Tarreau2e993902011-10-31 11:53:20 +01001105 /* get back there in 1 second or 1/20th of the slowstart interval,
1106 * whichever is greater, resulting in small 5% steps.
1107 */
Emeric Brun52a91d32017-08-31 14:41:55 +02001108 if (s->next_state == SRV_ST_STARTING)
Willy Tarreau2e993902011-10-31 11:53:20 +01001109 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1110 return t;
1111}
1112
Willy Tarreau894c6422017-10-04 15:58:52 +02001113/* returns the first NON-COMMENT tcp-check rule from list <list> or NULL if
1114 * none was found.
1115 */
Christopher Faulet5d503fc2020-03-30 20:34:34 +02001116static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Willy Tarreau894c6422017-10-04 15:58:52 +02001117{
1118 struct tcpcheck_rule *r;
1119
Christopher Faulet5d503fc2020-03-30 20:34:34 +02001120 list_for_each_entry(r, rules->list, list) {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001121 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
Willy Tarreau894c6422017-10-04 15:58:52 +02001122 return r;
1123 }
1124 return NULL;
1125}
1126
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +01001127/* returns the NON-COMMENT tcp-check rule from list <list> following <start> or
1128 * NULL if non was found. If <start> is NULL, it relies on
1129 * get_first_tcpcheck_rule().
1130 */
Christopher Faulet5d503fc2020-03-30 20:34:34 +02001131static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +01001132{
1133 struct tcpcheck_rule *r;
1134
1135 if (!start)
Christopher Faulet5d503fc2020-03-30 20:34:34 +02001136 return get_first_tcpcheck_rule(rules);
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +01001137
1138 r = LIST_NEXT(&start->list, typeof(r), list);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02001139 list_for_each_entry_from(r, rules->list, list) {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001140 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +01001141 return r;
1142 }
1143 return NULL;
1144}
1145
Willy Tarreau2e993902011-10-31 11:53:20 +01001146/*
Simon Horman98637e52014-06-20 12:30:16 +09001147 * establish a server health-check that makes use of a connection.
Simon Hormanb00d17a2014-06-13 16:18:16 +09001148 *
1149 * It can return one of :
Willy Tarreaue7dff022015-04-03 01:14:29 +02001150 * - SF_ERR_NONE if everything's OK and tcpcheck_main() was not called
1151 * - SF_ERR_UP if if everything's OK and tcpcheck_main() was called
1152 * - SF_ERR_SRVTO if there are no more servers
1153 * - SF_ERR_SRVCL if the connection was refused by the server
1154 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1155 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1156 * - SF_ERR_INTERNAL for any other purely internal errors
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02001157 * - SF_ERR_CHK_PORT if no port could be found to run a health check on an AF_INET* socket
Tim Düsterhus4896c442016-11-29 02:15:19 +01001158 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
Simon Hormanb00d17a2014-06-13 16:18:16 +09001159 * Note that we try to prevent the network stack from sending the ACK during the
1160 * connect() when a pure TCP check is used (without PROXY protocol).
1161 */
Simon Horman98637e52014-06-20 12:30:16 +09001162static int connect_conn_chk(struct task *t)
Simon Hormanb00d17a2014-06-13 16:18:16 +09001163{
1164 struct check *check = t->context;
1165 struct server *s = check->server;
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001166 struct conn_stream *cs = check->cs;
1167 struct connection *conn = cs_conn(cs);
Simon Hormanb00d17a2014-06-13 16:18:16 +09001168 struct protocol *proto;
1169 int ret;
Olivier Houchardfdcb0072019-05-06 18:32:29 +02001170 int connflags = 0;
Simon Hormanb00d17a2014-06-13 16:18:16 +09001171
Willy Tarreau00149122017-10-04 18:05:01 +02001172 /* we cannot have a connection here */
1173 if (conn)
1174 return SF_ERR_INTERNAL;
1175
Simon Hormanb00d17a2014-06-13 16:18:16 +09001176 /* prepare the check buffer.
1177 * This should not be used if check is the secondary agent check
1178 * of a server as s->proxy->check_req will relate to the
1179 * configuration of the primary check. Similarly, tcp-check uses
1180 * its own strings.
1181 */
1182 if (check->type && check->type != PR_O2_TCPCHK_CHK && !(check->state & CHK_ST_AGENT)) {
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001183 b_putblk(&check->bo, s->proxy->check_req, s->proxy->check_len);
Simon Hormanb00d17a2014-06-13 16:18:16 +09001184
Christopher Faulet811f78c2020-04-01 11:10:27 +02001185 /* we want to check if this host replies to HTTP requests
Simon Hormanb00d17a2014-06-13 16:18:16 +09001186 * so we'll send the request, and won't wake the checker up now.
1187 */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001188 if ((check->type) == PR_O2_HTTP_CHK) {
Cyril Bonté32602d22015-01-30 00:07:07 +01001189 /* prevent HTTP keep-alive when "http-check expect" is used */
1190 if (s->proxy->options2 & PR_O2_EXP_TYPE)
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001191 b_putist(&check->bo, ist("Connection: close\r\n"));
Christopher Faulet8acb1282020-04-09 08:44:06 +02001192
1193 /* If there is a body, add its content-length */
1194 if (s->proxy->check_body_len)
1195 chunk_appendf(&check->bo, "Content-Length: %s\r\n", ultoa(s->proxy->check_body_len));
1196
1197 /* Add configured headers */
1198 if (s->proxy->check_hdrs)
1199 b_putblk(&check->bo, s->proxy->check_hdrs, s->proxy->check_hdrs_len);
1200
1201 /* Add send-state header */
1202 if (s->proxy->options2 & PR_O2_CHK_SNDST)
1203 b_putblk(&check->bo, trash.area,
1204 httpchk_build_status_header(s, trash.area, trash.size));
1205
1206 /* end-of-header */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001207 b_putist(&check->bo, ist("\r\n"));
Christopher Faulet8acb1282020-04-09 08:44:06 +02001208
1209 /* Add the body */
1210 if (s->proxy->check_body)
1211 b_putblk(&check->bo, s->proxy->check_body, s->proxy->check_body_len);
1212
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001213 *b_tail(&check->bo) = '\0'; /* to make gdb output easier to read */
Simon Hormanb00d17a2014-06-13 16:18:16 +09001214 }
1215 }
1216
Willy Tarreauf411cce2017-10-04 16:21:19 +02001217 /* for tcp-checks, the initial connection setup is handled separately as
1218 * it may be sent to a specific port and not to the server's.
1219 */
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001220 if (check->type == PR_O2_TCPCHK_CHK) {
1221 /* tcpcheck initialisation */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02001222 check->current_step = NULL;
Willy Tarreauf411cce2017-10-04 16:21:19 +02001223 tcpcheck_main(check);
1224 return SF_ERR_UP;
1225 }
1226
Simon Hormanb00d17a2014-06-13 16:18:16 +09001227 /* prepare a new connection */
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001228 cs = check->cs = cs_new(NULL);
1229 if (!check->cs)
Willy Tarreau00149122017-10-04 18:05:01 +02001230 return SF_ERR_RESOURCE;
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001231 conn = cs->conn;
Olivier Houchard26e1a8f2018-09-12 15:15:12 +02001232 /* Maybe there were an older connection we were waiting on */
Willy Tarreau4f6516d2018-12-19 13:59:17 +01001233 check->wait_list.events = 0;
Olivier Houchardff1e9f32019-09-20 17:18:35 +02001234 tasklet_set_tid(check->wait_list.tasklet, tid);
1235
Simon Hormanb00d17a2014-06-13 16:18:16 +09001236
Willy Tarreauca79f592019-07-17 19:04:47 +02001237 if (!sockaddr_alloc(&conn->dst))
1238 return SF_ERR_RESOURCE;
1239
Simon Horman41f58762015-01-30 11:22:56 +09001240 if (is_addr(&check->addr)) {
Simon Hormanb00d17a2014-06-13 16:18:16 +09001241 /* we'll connect to the check addr specified on the server */
Willy Tarreaub3c81cb2019-07-17 16:54:52 +02001242 *conn->dst = check->addr;
Simon Hormanb00d17a2014-06-13 16:18:16 +09001243 }
1244 else {
1245 /* we'll connect to the addr on the server */
Willy Tarreaub3c81cb2019-07-17 16:54:52 +02001246 *conn->dst = s->addr;
Simon Hormanb00d17a2014-06-13 16:18:16 +09001247 }
1248
Alexander Liu2a54bb72019-05-22 19:44:48 +08001249 if (s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
1250 conn->send_proxy_ofs = 1;
1251 conn->flags |= CO_FL_SOCKS4;
1252 }
1253
Willy Tarreaub3c81cb2019-07-17 16:54:52 +02001254 proto = protocol_by_family(conn->dst->ss_family);
Olivier Houchard6377a002017-12-01 22:04:05 +01001255 conn->target = &s->obj_type;
1256
Willy Tarreaub3c81cb2019-07-17 16:54:52 +02001257 if ((conn->dst->ss_family == AF_INET) || (conn->dst->ss_family == AF_INET6)) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02001258 int i = 0;
1259
1260 i = srv_check_healthcheck_port(check);
Olivier Houchard6377a002017-12-01 22:04:05 +01001261 if (i == 0)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02001262 return SF_ERR_CHK_PORT;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02001263
Willy Tarreaub3c81cb2019-07-17 16:54:52 +02001264 set_host_port(conn->dst, i);
Simon Hormanb00d17a2014-06-13 16:18:16 +09001265 }
1266
Thierry FOURNIERbb2ae642015-01-14 11:31:49 +01001267 /* no client address */
Thierry FOURNIERbb2ae642015-01-14 11:31:49 +01001268
Willy Tarreaube373152018-09-06 11:45:30 +02001269 conn_prepare(conn, proto, check->xprt);
Olivier Houchardf67be932019-01-29 15:47:43 +01001270 if (conn_install_mux(conn, &mux_pt_ops, cs, s->proxy, NULL) < 0)
1271 return SF_ERR_RESOURCE;
Willy Tarreaube373152018-09-06 11:45:30 +02001272 cs_attach(cs, check, &check_conn_cb);
1273
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001274 /* only plain tcp check supports quick ACK */
1275 connflags |= (check->type ? CONNECT_HAS_DATA : CONNECT_DELACK_ALWAYS);
Simon Hormanb00d17a2014-06-13 16:18:16 +09001276
Willy Tarreaue7dff022015-04-03 01:14:29 +02001277 ret = SF_ERR_INTERNAL;
Olivier Houchardb68fda42017-08-04 18:39:01 +02001278 if (proto && proto->connect)
Olivier Houchardfdcb0072019-05-06 18:32:29 +02001279 ret = proto->connect(conn, connflags);
Willy Tarreau16257f62017-11-02 15:45:00 +01001280
Willy Tarreau16257f62017-11-02 15:45:00 +01001281
Olivier Houchard9130a962017-10-17 17:33:43 +02001282#ifdef USE_OPENSSL
Olivier Houcharda48437b2019-01-29 16:37:52 +01001283 if (ret == SF_ERR_NONE) {
1284 if (s->check.sni)
1285 ssl_sock_set_servername(conn, s->check.sni);
1286 if (s->check.alpn_str)
1287 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str,
1288 s->check.alpn_len);
1289 }
Olivier Houchard9130a962017-10-17 17:33:43 +02001290#endif
Willy Tarreauf4949772017-05-06 08:45:28 +02001291 if (s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
Simon Hormanb00d17a2014-06-13 16:18:16 +09001292 conn->send_proxy_ofs = 1;
1293 conn->flags |= CO_FL_SEND_PROXY;
Olivier Houchard37d78972019-12-30 15:13:42 +01001294 }
1295 if (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4) &&
1296 conn_ctrl_ready(conn)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +02001297 if (xprt_add_hs(conn) < 0)
1298 ret = SF_ERR_RESOURCE;
Simon Hormanb00d17a2014-06-13 16:18:16 +09001299 }
1300
1301 return ret;
1302}
1303
Simon Horman98637e52014-06-20 12:30:16 +09001304static struct list pid_list = LIST_HEAD_INIT(pid_list);
Willy Tarreaubafbe012017-11-24 17:34:44 +01001305static struct pool_head *pool_head_pid_list;
Willy Tarreau86abe442018-11-25 20:12:18 +01001306__decl_spinlock(pid_list_lock);
Simon Horman98637e52014-06-20 12:30:16 +09001307
1308void block_sigchld(void)
1309{
1310 sigset_t set;
1311 sigemptyset(&set);
1312 sigaddset(&set, SIGCHLD);
William Lallemand6e1796e2018-06-07 11:23:40 +02001313 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
Simon Horman98637e52014-06-20 12:30:16 +09001314}
1315
1316void unblock_sigchld(void)
1317{
1318 sigset_t set;
1319 sigemptyset(&set);
Willy Tarreauebc92442016-06-21 17:29:46 +02001320 sigaddset(&set, SIGCHLD);
William Lallemand6e1796e2018-06-07 11:23:40 +02001321 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Simon Horman98637e52014-06-20 12:30:16 +09001322}
1323
Simon Horman98637e52014-06-20 12:30:16 +09001324static struct pid_list *pid_list_add(pid_t pid, struct task *t)
1325{
1326 struct pid_list *elem;
1327 struct check *check = t->context;
1328
Willy Tarreaubafbe012017-11-24 17:34:44 +01001329 elem = pool_alloc(pool_head_pid_list);
Simon Horman98637e52014-06-20 12:30:16 +09001330 if (!elem)
1331 return NULL;
1332 elem->pid = pid;
1333 elem->t = t;
1334 elem->exited = 0;
1335 check->curpid = elem;
1336 LIST_INIT(&elem->list);
Christopher Fauletcfda8472017-10-20 15:40:23 +02001337
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001338 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
Simon Horman98637e52014-06-20 12:30:16 +09001339 LIST_ADD(&pid_list, &elem->list);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001340 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Fauletcfda8472017-10-20 15:40:23 +02001341
Simon Horman98637e52014-06-20 12:30:16 +09001342 return elem;
1343}
1344
Simon Horman98637e52014-06-20 12:30:16 +09001345static void pid_list_del(struct pid_list *elem)
1346{
1347 struct check *check;
1348
1349 if (!elem)
1350 return;
1351
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001352 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
Simon Horman98637e52014-06-20 12:30:16 +09001353 LIST_DEL(&elem->list);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001354 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Fauletcfda8472017-10-20 15:40:23 +02001355
Simon Horman98637e52014-06-20 12:30:16 +09001356 if (!elem->exited)
1357 kill(elem->pid, SIGTERM);
1358
1359 check = elem->t->context;
1360 check->curpid = NULL;
Willy Tarreaubafbe012017-11-24 17:34:44 +01001361 pool_free(pool_head_pid_list, elem);
Simon Horman98637e52014-06-20 12:30:16 +09001362}
1363
1364/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
1365static void pid_list_expire(pid_t pid, int status)
1366{
1367 struct pid_list *elem;
1368
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001369 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
Simon Horman98637e52014-06-20 12:30:16 +09001370 list_for_each_entry(elem, &pid_list, list) {
1371 if (elem->pid == pid) {
1372 elem->t->expire = now_ms;
1373 elem->status = status;
1374 elem->exited = 1;
Cyril Bonté9dbcfab2014-08-07 01:55:39 +02001375 task_wakeup(elem->t, TASK_WOKEN_IO);
Christopher Fauletcfda8472017-10-20 15:40:23 +02001376 break;
Simon Horman98637e52014-06-20 12:30:16 +09001377 }
1378 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001379 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Simon Horman98637e52014-06-20 12:30:16 +09001380}
1381
Willy Tarreau48d6bf22016-06-21 16:27:34 +02001382static void sigchld_handler(struct sig_handler *sh)
Simon Horman98637e52014-06-20 12:30:16 +09001383{
1384 pid_t pid;
1385 int status;
Willy Tarreau48d6bf22016-06-21 16:27:34 +02001386
Simon Horman98637e52014-06-20 12:30:16 +09001387 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
1388 pid_list_expire(pid, status);
1389}
1390
Willy Tarreau48d6bf22016-06-21 16:27:34 +02001391static int init_pid_list(void)
1392{
Willy Tarreaubafbe012017-11-24 17:34:44 +01001393 if (pool_head_pid_list != NULL)
Simon Horman98637e52014-06-20 12:30:16 +09001394 /* Nothing to do */
1395 return 0;
1396
Willy Tarreau48d6bf22016-06-21 16:27:34 +02001397 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001398 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
1399 strerror(errno));
Simon Horman98637e52014-06-20 12:30:16 +09001400 return 1;
1401 }
1402
Willy Tarreaubafbe012017-11-24 17:34:44 +01001403 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
1404 if (pool_head_pid_list == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001405 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
1406 strerror(errno));
Simon Horman98637e52014-06-20 12:30:16 +09001407 return 1;
1408 }
1409
1410 return 0;
1411}
1412
Cyril Bontéac92a062014-12-27 22:28:38 +01001413/* helper macro to set an environment variable and jump to a specific label on failure. */
1414#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001415
1416/*
Cyril Bontéac92a062014-12-27 22:28:38 +01001417 * helper function to allocate enough memory to store an environment variable.
1418 * It will also check that the environment variable is updatable, and silently
1419 * fail if not.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001420 */
Cyril Bontéac92a062014-12-27 22:28:38 +01001421static int extchk_setenv(struct check *check, int idx, const char *value)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001422{
1423 int len, ret;
Cyril Bontéac92a062014-12-27 22:28:38 +01001424 char *envname;
1425 int vmaxlen;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001426
Cyril Bontéac92a062014-12-27 22:28:38 +01001427 if (idx < 0 || idx >= EXTCHK_SIZE) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001428 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
Cyril Bontéac92a062014-12-27 22:28:38 +01001429 return 1;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001430 }
Cyril Bontéac92a062014-12-27 22:28:38 +01001431
1432 envname = extcheck_envs[idx].name;
1433 vmaxlen = extcheck_envs[idx].vmaxlen;
1434
1435 /* Check if the environment variable is already set, and silently reject
1436 * the update if this one is not updatable. */
1437 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
1438 return 0;
1439
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001440 /* Instead of sending NOT_USED, sending an empty value is preferable */
1441 if (strcmp(value, "NOT_USED") == 0) {
1442 value = "";
1443 }
Cyril Bontéac92a062014-12-27 22:28:38 +01001444
1445 len = strlen(envname) + 1;
1446 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
1447 len += strlen(value);
1448 else
1449 len += vmaxlen;
1450
1451 if (!check->envp[idx])
1452 check->envp[idx] = malloc(len + 1);
1453
1454 if (!check->envp[idx]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001455 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001456 return 1;
1457 }
Cyril Bontéac92a062014-12-27 22:28:38 +01001458 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001459 if (ret < 0) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001460 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001461 return 1;
1462 }
Cyril Bontéac92a062014-12-27 22:28:38 +01001463 else if (ret > len) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001464 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001465 return 1;
1466 }
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001467 return 0;
1468}
Simon Horman98637e52014-06-20 12:30:16 +09001469
1470static int prepare_external_check(struct check *check)
1471{
1472 struct server *s = check->server;
1473 struct proxy *px = s->proxy;
1474 struct listener *listener = NULL, *l;
1475 int i;
Simon Horman98637e52014-06-20 12:30:16 +09001476 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001477 char buf[256];
Simon Horman98637e52014-06-20 12:30:16 +09001478
1479 list_for_each_entry(l, &px->conf.listeners, by_fe)
1480 /* Use the first INET, INET6 or UNIX listener */
1481 if (l->addr.ss_family == AF_INET ||
1482 l->addr.ss_family == AF_INET6 ||
1483 l->addr.ss_family == AF_UNIX) {
1484 listener = l;
1485 break;
1486 }
1487
Simon Horman98637e52014-06-20 12:30:16 +09001488 check->curpid = NULL;
Cyril Bontéac92a062014-12-27 22:28:38 +01001489 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
1490 if (!check->envp) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001491 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
Cyril Bontéac92a062014-12-27 22:28:38 +01001492 goto err;
1493 }
Simon Horman98637e52014-06-20 12:30:16 +09001494
Cyril Bontéac92a062014-12-27 22:28:38 +01001495 check->argv = calloc(6, sizeof(char *));
1496 if (!check->argv) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001497 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
Simon Horman98637e52014-06-20 12:30:16 +09001498 goto err;
Cyril Bontéac92a062014-12-27 22:28:38 +01001499 }
Simon Horman98637e52014-06-20 12:30:16 +09001500
1501 check->argv[0] = px->check_command;
1502
Cyril Bonté777be862014-12-02 21:21:35 +01001503 if (!listener) {
1504 check->argv[1] = strdup("NOT_USED");
1505 check->argv[2] = strdup("NOT_USED");
1506 }
1507 else if (listener->addr.ss_family == AF_INET ||
Simon Horman98637e52014-06-20 12:30:16 +09001508 listener->addr.ss_family == AF_INET6) {
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001509 addr_to_str(&listener->addr, buf, sizeof(buf));
1510 check->argv[1] = strdup(buf);
1511 port_to_str(&listener->addr, buf, sizeof(buf));
1512 check->argv[2] = strdup(buf);
Cyril Bonté777be862014-12-02 21:21:35 +01001513 }
1514 else if (listener->addr.ss_family == AF_UNIX) {
Simon Horman98637e52014-06-20 12:30:16 +09001515 const struct sockaddr_un *un;
1516
1517 un = (struct sockaddr_un *)&listener->addr;
1518 check->argv[1] = strdup(un->sun_path);
1519 check->argv[2] = strdup("NOT_USED");
Cyril Bonté777be862014-12-02 21:21:35 +01001520 }
1521 else {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001522 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
Simon Horman98637e52014-06-20 12:30:16 +09001523 goto err;
1524 }
1525
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001526 if (!check->argv[1] || !check->argv[2]) {
1527 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1528 goto err;
1529 }
1530
1531 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
1532 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
1533 if (!check->argv[3] || !check->argv[4]) {
1534 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1535 goto err;
1536 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001537
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001538 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
Willy Tarreau04276f32017-01-06 17:41:29 +01001539 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001540 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Simon Horman98637e52014-06-20 12:30:16 +09001541
Cyril Bontéac92a062014-12-27 22:28:38 +01001542 for (i = 0; i < 5; i++) {
1543 if (!check->argv[i]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01001544 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
Simon Horman98637e52014-06-20 12:30:16 +09001545 goto err;
Cyril Bontéac92a062014-12-27 22:28:38 +01001546 }
1547 }
Simon Horman98637e52014-06-20 12:30:16 +09001548
Cyril Bontéac92a062014-12-27 22:28:38 +01001549 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001550 /* Add proxy environment variables */
Cyril Bontéac92a062014-12-27 22:28:38 +01001551 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
1552 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
1553 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
1554 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001555 /* Add server environment variables */
Cyril Bontéac92a062014-12-27 22:28:38 +01001556 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
1557 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
1558 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
1559 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
1560 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
1561 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
1562
1563 /* Ensure that we don't leave any hole in check->envp */
1564 for (i = 0; i < EXTCHK_SIZE; i++)
1565 if (!check->envp[i])
1566 EXTCHK_SETENV(check, i, "", err);
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001567
Cyril Bonté99c5bf52014-08-07 01:55:38 +02001568 return 1;
Simon Horman98637e52014-06-20 12:30:16 +09001569err:
1570 if (check->envp) {
Cyril Bontéac92a062014-12-27 22:28:38 +01001571 for (i = 0; i < EXTCHK_SIZE; i++)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001572 free(check->envp[i]);
Simon Horman98637e52014-06-20 12:30:16 +09001573 free(check->envp);
1574 check->envp = NULL;
1575 }
1576
1577 if (check->argv) {
1578 for (i = 1; i < 5; i++)
1579 free(check->argv[i]);
1580 free(check->argv);
1581 check->argv = NULL;
1582 }
Cyril Bonté99c5bf52014-08-07 01:55:38 +02001583 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001584}
1585
Simon Hormanb00d17a2014-06-13 16:18:16 +09001586/*
Simon Horman98637e52014-06-20 12:30:16 +09001587 * establish a server health-check that makes use of a process.
1588 *
1589 * It can return one of :
Willy Tarreaue7dff022015-04-03 01:14:29 +02001590 * - SF_ERR_NONE if everything's OK
Willy Tarreaue7dff022015-04-03 01:14:29 +02001591 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
Tim Düsterhus4896c442016-11-29 02:15:19 +01001592 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
Simon Horman98637e52014-06-20 12:30:16 +09001593 *
1594 * Blocks and then unblocks SIGCHLD
1595 */
1596static int connect_proc_chk(struct task *t)
1597{
Cyril Bontéac92a062014-12-27 22:28:38 +01001598 char buf[256];
Simon Horman98637e52014-06-20 12:30:16 +09001599 struct check *check = t->context;
1600 struct server *s = check->server;
1601 struct proxy *px = s->proxy;
1602 int status;
1603 pid_t pid;
1604
Willy Tarreaue7dff022015-04-03 01:14:29 +02001605 status = SF_ERR_RESOURCE;
Simon Horman98637e52014-06-20 12:30:16 +09001606
1607 block_sigchld();
1608
1609 pid = fork();
1610 if (pid < 0) {
Willy Tarreaud96f1122019-12-03 07:07:36 +01001611 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
1612 (global.tune.options & GTUNE_INSECURE_FORK) ?
1613 "" : " (likely caused by missing 'insecure-fork-wanted')",
Christopher Faulet767a84b2017-11-24 16:50:31 +01001614 strerror(errno));
Simon Horman98637e52014-06-20 12:30:16 +09001615 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
1616 goto out;
1617 }
1618 if (pid == 0) {
1619 /* Child */
1620 extern char **environ;
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001621 struct rlimit limit;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001622 int fd;
1623
1624 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
1625 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
1626
Willy Tarreau2555ccf2019-02-21 22:22:06 +01001627 my_closefrom(fd);
Willy Tarreaub7b24782016-06-21 15:32:29 +02001628
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001629 /* restore the initial FD limits */
1630 limit.rlim_cur = rlim_fd_cur_at_boot;
1631 limit.rlim_max = rlim_fd_max_at_boot;
1632 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
1633 getrlimit(RLIMIT_NOFILE, &limit);
1634 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
1635 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
1636 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
1637 }
1638
Simon Horman98637e52014-06-20 12:30:16 +09001639 environ = check->envp;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001640
1641 /* Update some environment variables and command args: curconn, server addr and server port */
Cyril Bontéac92a062014-12-27 22:28:38 +01001642 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001643
1644 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
1645 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
1646
1647 *check->argv[4] = 0;
1648 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1649 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
1650 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
1651
Willy Tarreau2df8cad2019-07-01 07:51:29 +02001652 haproxy_unblock_signals();
Simon Horman98637e52014-06-20 12:30:16 +09001653 execvp(px->check_command, check->argv);
Christopher Faulet767a84b2017-11-24 16:50:31 +01001654 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
1655 strerror(errno));
Simon Horman98637e52014-06-20 12:30:16 +09001656 exit(-1);
1657 }
1658
1659 /* Parent */
1660 if (check->result == CHK_RES_UNKNOWN) {
1661 if (pid_list_add(pid, t) != NULL) {
1662 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
1663
1664 if (px->timeout.check && px->timeout.connect) {
1665 int t_con = tick_add(now_ms, px->timeout.connect);
1666 t->expire = tick_first(t->expire, t_con);
1667 }
Willy Tarreaue7dff022015-04-03 01:14:29 +02001668 status = SF_ERR_NONE;
Simon Horman98637e52014-06-20 12:30:16 +09001669 goto out;
1670 }
1671 else {
1672 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
1673 }
1674 kill(pid, SIGTERM); /* process creation error */
1675 }
1676 else
1677 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
1678
1679out:
1680 unblock_sigchld();
1681 return status;
1682}
1683
1684/*
Willy Tarreau1e62e2a2017-10-04 15:07:02 +02001685 * manages a server health-check that uses an external process. Returns
Willy Tarreaubaaee002006-06-26 02:48:02 +02001686 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001687 *
1688 * Please do NOT place any return statement in this function and only leave
1689 * via the out_unlock label.
Willy Tarreaubaaee002006-06-26 02:48:02 +02001690 */
Olivier Houchard9f6af332018-05-25 14:04:04 +02001691static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Simon Horman98637e52014-06-20 12:30:16 +09001692{
Olivier Houchard9f6af332018-05-25 14:04:04 +02001693 struct check *check = context;
Simon Horman98637e52014-06-20 12:30:16 +09001694 struct server *s = check->server;
Simon Horman98637e52014-06-20 12:30:16 +09001695 int rv;
1696 int ret;
1697 int expired = tick_is_expired(t->expire, now_ms);
1698
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001699 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
Simon Horman98637e52014-06-20 12:30:16 +09001700 if (!(check->state & CHK_ST_INPROGRESS)) {
1701 /* no check currently running */
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001702 if (!expired) /* woke up too early */
1703 goto out_unlock;
Simon Horman98637e52014-06-20 12:30:16 +09001704
1705 /* we don't send any health-checks when the proxy is
1706 * stopped, the server should not be checked or the check
1707 * is disabled.
1708 */
1709 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
1710 s->proxy->state == PR_STSTOPPED)
1711 goto reschedule;
1712
1713 /* we'll initiate a new check */
1714 set_server_check_status(check, HCHK_STATUS_START, NULL);
1715
1716 check->state |= CHK_ST_INPROGRESS;
1717
Simon Hormandbf70192015-01-30 11:22:53 +09001718 ret = connect_proc_chk(t);
Willy Tarreaud7c3fbd2017-10-04 15:19:26 +02001719 if (ret == SF_ERR_NONE) {
Willy Tarreau1e62e2a2017-10-04 15:07:02 +02001720 /* the process was forked, we allow up to min(inter,
1721 * timeout.connect) for it to report its status, but
1722 * only when timeout.check is set as it may be to short
1723 * for a full check otherwise.
Simon Horman98637e52014-06-20 12:30:16 +09001724 */
1725 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
1726
1727 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
1728 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
1729 t->expire = tick_first(t->expire, t_con);
1730 }
Christopher Faulet88ce5d12017-10-20 15:41:18 +02001731 task_set_affinity(t, tid_bit);
Simon Horman98637e52014-06-20 12:30:16 +09001732 goto reschedule;
Simon Horman98637e52014-06-20 12:30:16 +09001733 }
1734
Willy Tarreau1e62e2a2017-10-04 15:07:02 +02001735 /* here, we failed to start the check */
Simon Horman98637e52014-06-20 12:30:16 +09001736
1737 check->state &= ~CHK_ST_INPROGRESS;
1738 check_notify_failure(check);
1739
1740 /* we allow up to min(inter, timeout.connect) for a connection
1741 * to establish but only when timeout.check is set
1742 * as it may be to short for a full check otherwise
1743 */
1744 while (tick_is_expired(t->expire, now_ms)) {
1745 int t_con;
1746
1747 t_con = tick_add(t->expire, s->proxy->timeout.connect);
1748 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
1749
1750 if (s->proxy->timeout.check)
1751 t->expire = tick_first(t->expire, t_con);
1752 }
1753 }
1754 else {
1755 /* there was a test running.
1756 * First, let's check whether there was an uncaught error,
1757 * which can happen on connect timeout or error.
1758 */
1759 if (check->result == CHK_RES_UNKNOWN) {
1760 /* good connection is enough for pure TCP check */
1761 struct pid_list *elem = check->curpid;
1762 int status = HCHK_STATUS_UNKNOWN;
1763
1764 if (elem->exited) {
1765 status = elem->status; /* Save in case the process exits between use below */
1766 if (!WIFEXITED(status))
1767 check->code = -1;
1768 else
1769 check->code = WEXITSTATUS(status);
1770 if (!WIFEXITED(status) || WEXITSTATUS(status))
1771 status = HCHK_STATUS_PROCERR;
1772 else
1773 status = HCHK_STATUS_PROCOK;
1774 } else if (expired) {
1775 status = HCHK_STATUS_PROCTOUT;
Christopher Faulet767a84b2017-11-24 16:50:31 +01001776 ha_warning("kill %d\n", (int)elem->pid);
Simon Horman98637e52014-06-20 12:30:16 +09001777 kill(elem->pid, SIGTERM);
1778 }
1779 set_server_check_status(check, status, NULL);
1780 }
1781
1782 if (check->result == CHK_RES_FAILED) {
1783 /* a failure or timeout detected */
1784 check_notify_failure(check);
1785 }
1786 else if (check->result == CHK_RES_CONDPASS) {
1787 /* check is OK but asks for stopping mode */
1788 check_notify_stopping(check);
1789 }
1790 else if (check->result == CHK_RES_PASSED) {
1791 /* a success was detected */
1792 check_notify_success(check);
1793 }
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001794 task_set_affinity(t, 1);
Simon Horman98637e52014-06-20 12:30:16 +09001795 check->state &= ~CHK_ST_INPROGRESS;
1796
1797 pid_list_del(check->curpid);
1798
1799 rv = 0;
1800 if (global.spread_checks > 0) {
1801 rv = srv_getinter(check) * global.spread_checks / 100;
Willy Tarreau5a6d3e72020-03-08 17:53:53 +01001802 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Simon Horman98637e52014-06-20 12:30:16 +09001803 }
1804 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
1805 }
1806
1807 reschedule:
1808 while (tick_is_expired(t->expire, now_ms))
1809 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001810
1811 out_unlock:
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001812 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Simon Horman98637e52014-06-20 12:30:16 +09001813 return t;
1814}
1815
1816/*
1817 * manages a server health-check that uses a connection. Returns
1818 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001819 *
1820 * Please do NOT place any return statement in this function and only leave
1821 * via the out_unlock label.
Simon Horman98637e52014-06-20 12:30:16 +09001822 */
Olivier Houchard9f6af332018-05-25 14:04:04 +02001823static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001824{
Olivier Houchard9f6af332018-05-25 14:04:04 +02001825 struct check *check = context;
Olivier Houchardc98aa1f2019-01-11 18:17:17 +01001826 struct proxy *proxy = check->proxy;
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001827 struct conn_stream *cs = check->cs;
1828 struct connection *conn = cs_conn(cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001829 int rv;
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001830 int ret;
Willy Tarreauacbdc7a2012-11-23 14:02:10 +01001831 int expired = tick_is_expired(t->expire, now_ms);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001832
Olivier Houchard0923fa42019-01-11 18:43:04 +01001833 if (check->server)
1834 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
Willy Tarreau2c115e52013-12-11 19:41:16 +01001835 if (!(check->state & CHK_ST_INPROGRESS)) {
Willy Tarreau5a78f362012-11-23 12:47:05 +01001836 /* no check currently running */
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001837 if (!expired) /* woke up too early */
1838 goto out_unlock;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001839
Simon Horman671b6f02013-11-25 10:46:39 +09001840 /* we don't send any health-checks when the proxy is
1841 * stopped, the server should not be checked or the check
1842 * is disabled.
Willy Tarreaubaaee002006-06-26 02:48:02 +02001843 */
Willy Tarreau0d924cc2013-12-11 21:26:24 +01001844 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
Olivier Houchardc98aa1f2019-01-11 18:17:17 +01001845 proxy->state == PR_STSTOPPED)
Willy Tarreau5a78f362012-11-23 12:47:05 +01001846 goto reschedule;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001847
1848 /* we'll initiate a new check */
Simon Horman4a741432013-02-23 15:35:38 +09001849 set_server_check_status(check, HCHK_STATUS_START, NULL);
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001850
Willy Tarreau2c115e52013-12-11 19:41:16 +01001851 check->state |= CHK_ST_INPROGRESS;
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001852 b_reset(&check->bi);
1853 b_reset(&check->bo);
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001854
Olivier Houchardaebeff72019-11-29 16:18:51 +01001855 task_set_affinity(t, tid_bit);
Simon Hormandbf70192015-01-30 11:22:53 +09001856 ret = connect_conn_chk(t);
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001857 cs = check->cs;
1858 conn = cs_conn(cs);
Willy Tarreau00149122017-10-04 18:05:01 +02001859
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001860 switch (ret) {
Willy Tarreaue7dff022015-04-03 01:14:29 +02001861 case SF_ERR_UP:
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001862 goto out_unlock;
1863
Willy Tarreaue7dff022015-04-03 01:14:29 +02001864 case SF_ERR_NONE:
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001865 /* we allow up to min(inter, timeout.connect) for a connection
1866 * to establish but only when timeout.check is set
1867 * as it may be to short for a full check otherwise
1868 */
Simon Horman4a741432013-02-23 15:35:38 +09001869 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Olivier Houchardc98aa1f2019-01-11 18:17:17 +01001870 if (proxy->timeout.check && proxy->timeout.connect) {
1871 int t_con = tick_add(now_ms, proxy->timeout.connect);
Willy Tarreaufb56aab2012-09-28 14:40:02 +02001872 t->expire = tick_first(t->expire, t_con);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001873 }
Willy Tarreau06559ac2013-12-05 01:53:08 +01001874
Willy Tarreaucc705a62019-09-05 17:51:30 +02001875 if (check->type) {
1876 /* send the request if we have one. We avoid receiving
1877 * if not connected, unless we didn't subscribe for
1878 * sending since otherwise we won't be woken up.
1879 */
1880 __event_srv_chk_w(cs);
Willy Tarreau911db9b2020-01-23 16:27:54 +01001881 if (!(conn->flags & CO_FL_WAIT_XPRT) ||
Willy Tarreauc5940392019-09-05 17:38:40 +02001882 !(check->wait_list.events & SUB_RETRY_SEND))
1883 __event_srv_chk_r(cs);
Willy Tarreaucc705a62019-09-05 17:51:30 +02001884 }
Willy Tarreau06559ac2013-12-05 01:53:08 +01001885
Willy Tarreau5a78f362012-11-23 12:47:05 +01001886 goto reschedule;
1887
Willy Tarreaue7dff022015-04-03 01:14:29 +02001888 case SF_ERR_SRVTO: /* ETIMEDOUT */
1889 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
Willy Tarreau00149122017-10-04 18:05:01 +02001890 if (conn)
1891 conn->flags |= CO_FL_ERROR;
Willy Tarreaub5259bf2017-10-04 14:47:29 +02001892 chk_report_conn_err(check, errno, 0);
Willy Tarreau5a78f362012-11-23 12:47:05 +01001893 break;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02001894 /* should share same code than cases below */
1895 case SF_ERR_CHK_PORT:
1896 check->state |= CHK_ST_PORT_MISS;
Willy Tarreaue7dff022015-04-03 01:14:29 +02001897 case SF_ERR_PRXCOND:
1898 case SF_ERR_RESOURCE:
1899 case SF_ERR_INTERNAL:
Willy Tarreau00149122017-10-04 18:05:01 +02001900 if (conn)
1901 conn->flags |= CO_FL_ERROR;
1902 chk_report_conn_err(check, conn ? 0 : ENOMEM, 0);
Willy Tarreau5a78f362012-11-23 12:47:05 +01001903 break;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001904 }
1905
Willy Tarreau5a78f362012-11-23 12:47:05 +01001906 /* here, we have seen a synchronous error, no fd was allocated */
Olivier Houchardaebeff72019-11-29 16:18:51 +01001907 task_set_affinity(t, MAX_THREADS_MASK);
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001908 if (cs) {
Olivier Houchard49065542019-05-31 19:20:36 +02001909 if (check->wait_list.events)
1910 cs->conn->xprt->unsubscribe(cs->conn,
1911 cs->conn->xprt_ctx,
1912 check->wait_list.events,
1913 &check->wait_list);
1914 /* We may have been scheduled to run, and the
1915 * I/O handler expects to have a cs, so remove
1916 * the tasklet
1917 */
Willy Tarreau86eded62019-06-14 14:47:49 +02001918 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001919 cs_destroy(cs);
1920 cs = check->cs = NULL;
1921 conn = NULL;
Olivier Houchard390485a2017-10-24 19:03:30 +02001922 }
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001923
Willy Tarreau2c115e52013-12-11 19:41:16 +01001924 check->state &= ~CHK_ST_INPROGRESS;
Willy Tarreau4eec5472014-05-20 22:32:27 +02001925 check_notify_failure(check);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001926
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001927 /* we allow up to min(inter, timeout.connect) for a connection
1928 * to establish but only when timeout.check is set
1929 * as it may be to short for a full check otherwise
1930 */
Willy Tarreau0c303ee2008-07-07 00:09:58 +02001931 while (tick_is_expired(t->expire, now_ms)) {
1932 int t_con;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001933
Olivier Houchardc98aa1f2019-01-11 18:17:17 +01001934 t_con = tick_add(t->expire, proxy->timeout.connect);
Simon Horman4a741432013-02-23 15:35:38 +09001935 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Olivier Houchardc98aa1f2019-01-11 18:17:17 +01001936 if (proxy->timeout.check)
Willy Tarreau0c303ee2008-07-07 00:09:58 +02001937 t->expire = tick_first(t->expire, t_con);
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001938 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02001939 }
1940 else {
Willy Tarreauf1503172012-09-28 19:39:36 +02001941 /* there was a test running.
1942 * First, let's check whether there was an uncaught error,
1943 * which can happen on connect timeout or error.
1944 */
Simon Hormanccaabcd2014-06-20 12:29:47 +09001945 if (check->result == CHK_RES_UNKNOWN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +01001946 /* good connection is enough for pure TCP check */
Willy Tarreau911db9b2020-01-23 16:27:54 +01001947 if (!(conn->flags & CO_FL_WAIT_XPRT) && !check->type) {
Christopher Fauletf61f33a2020-03-27 18:55:49 +01001948 if (check->use_ssl == 1)
Simon Horman4a741432013-02-23 15:35:38 +09001949 set_server_check_status(check, HCHK_STATUS_L6OK, NULL);
Willy Tarreauf1503172012-09-28 19:39:36 +02001950 else
Simon Horman4a741432013-02-23 15:35:38 +09001951 set_server_check_status(check, HCHK_STATUS_L4OK, NULL);
Willy Tarreauacbdc7a2012-11-23 14:02:10 +01001952 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +02001953 else if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
Willy Tarreaub5259bf2017-10-04 14:47:29 +02001954 chk_report_conn_err(check, 0, expired);
Willy Tarreauf1503172012-09-28 19:39:36 +02001955 }
Willy Tarreau74fa7fb2012-11-23 14:43:49 +01001956 else
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001957 goto out_unlock; /* timeout not reached, wait again */
Willy Tarreauf1503172012-09-28 19:39:36 +02001958 }
1959
Willy Tarreau74fa7fb2012-11-23 14:43:49 +01001960 /* check complete or aborted */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001961
1962 check->current_step = NULL;
1963 if (check->sess != NULL) {
1964 session_free(check->sess);
1965 check->sess = NULL;
1966 }
1967
Willy Tarreau00149122017-10-04 18:05:01 +02001968 if (conn && conn->xprt) {
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001969 /* The check was aborted and the connection was not yet closed.
1970 * This can happen upon timeout, or when an external event such
1971 * as a failed response coupled with "observe layer7" caused the
1972 * server state to be suddenly changed.
1973 */
Willy Tarreaud85c4852015-03-13 00:40:28 +01001974 conn_sock_drain(conn);
Willy Tarreaua553ae92017-10-05 18:52:17 +02001975 cs_close(cs);
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001976 }
1977
Willy Tarreauac59f362017-10-08 11:10:19 +02001978 if (cs) {
Olivier Houchard49065542019-05-31 19:20:36 +02001979 if (check->wait_list.events)
1980 cs->conn->xprt->unsubscribe(cs->conn,
1981 cs->conn->xprt_ctx,
1982 check->wait_list.events,
1983 &check->wait_list);
1984 /* We may have been scheduled to run, and the
Willy Tarreau86eded62019-06-14 14:47:49 +02001985 * I/O handler expects to have a cs, so remove
1986 * the tasklet
1987 */
1988 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
Olivier Houchard9aaf7782017-09-13 18:30:23 +02001989 cs_destroy(cs);
1990 cs = check->cs = NULL;
1991 conn = NULL;
Willy Tarreau00149122017-10-04 18:05:01 +02001992 }
1993
Olivier Houchard0923fa42019-01-11 18:43:04 +01001994 if (check->server) {
1995 if (check->result == CHK_RES_FAILED) {
1996 /* a failure or timeout detected */
1997 check_notify_failure(check);
1998 }
1999 else if (check->result == CHK_RES_CONDPASS) {
2000 /* check is OK but asks for stopping mode */
2001 check_notify_stopping(check);
2002 }
2003 else if (check->result == CHK_RES_PASSED) {
2004 /* a success was detected */
2005 check_notify_success(check);
2006 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02002007 }
Christopher Faulet88ce5d12017-10-20 15:41:18 +02002008 task_set_affinity(t, MAX_THREADS_MASK);
Willy Tarreau2c115e52013-12-11 19:41:16 +01002009 check->state &= ~CHK_ST_INPROGRESS;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002010
Olivier Houchard0923fa42019-01-11 18:43:04 +01002011 if (check->server) {
2012 rv = 0;
2013 if (global.spread_checks > 0) {
2014 rv = srv_getinter(check) * global.spread_checks / 100;
Willy Tarreau5a6d3e72020-03-08 17:53:53 +01002015 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Olivier Houchard0923fa42019-01-11 18:43:04 +01002016 }
2017 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Willy Tarreaubaaee002006-06-26 02:48:02 +02002018 }
Willy Tarreaubaaee002006-06-26 02:48:02 +02002019 }
Willy Tarreau5a78f362012-11-23 12:47:05 +01002020
2021 reschedule:
2022 while (tick_is_expired(t->expire, now_ms))
Simon Horman4a741432013-02-23 15:35:38 +09002023 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Willy Tarreau62ac84f2017-11-05 10:11:13 +01002024 out_unlock:
Olivier Houchard0923fa42019-01-11 18:43:04 +01002025 if (check->server)
2026 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Willy Tarreau26c25062009-03-08 09:38:41 +01002027 return t;
Willy Tarreaubaaee002006-06-26 02:48:02 +02002028}
2029
Simon Horman98637e52014-06-20 12:30:16 +09002030/*
2031 * manages a server health-check. Returns
2032 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
2033 */
Olivier Houchard9f6af332018-05-25 14:04:04 +02002034static struct task *process_chk(struct task *t, void *context, unsigned short state)
Simon Horman98637e52014-06-20 12:30:16 +09002035{
Olivier Houchard9f6af332018-05-25 14:04:04 +02002036 struct check *check = context;
Simon Horman98637e52014-06-20 12:30:16 +09002037
2038 if (check->type == PR_O2_EXT_CHK)
Olivier Houchard9f6af332018-05-25 14:04:04 +02002039 return process_chk_proc(t, context, state);
2040 return process_chk_conn(t, context, state);
Baptiste Assmanna68ca962015-04-14 01:15:08 +02002041
Simon Horman98637e52014-06-20 12:30:16 +09002042}
2043
Simon Horman5c942422013-11-25 10:46:32 +09002044static int start_check_task(struct check *check, int mininter,
2045 int nbcheck, int srvpos)
2046{
2047 struct task *t;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02002048 unsigned long thread_mask = MAX_THREADS_MASK;
2049
2050 if (check->type == PR_O2_EXT_CHK)
2051 thread_mask = 1;
2052
Simon Horman5c942422013-11-25 10:46:32 +09002053 /* task for the check */
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02002054 if ((t = task_new(thread_mask)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002055 ha_alert("Starting [%s:%s] check: out of memory.\n",
2056 check->server->proxy->id, check->server->id);
Simon Horman5c942422013-11-25 10:46:32 +09002057 return 0;
2058 }
2059
2060 check->task = t;
2061 t->process = process_chk;
2062 t->context = check;
2063
Willy Tarreau1746eec2014-04-25 10:46:47 +02002064 if (mininter < srv_getinter(check))
2065 mininter = srv_getinter(check);
2066
2067 if (global.max_spread_checks && mininter > global.max_spread_checks)
2068 mininter = global.max_spread_checks;
2069
Simon Horman5c942422013-11-25 10:46:32 +09002070 /* check this every ms */
Willy Tarreau1746eec2014-04-25 10:46:47 +02002071 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
Simon Horman5c942422013-11-25 10:46:32 +09002072 check->start = now;
2073 task_queue(t);
2074
2075 return 1;
2076}
2077
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002078/*
2079 * Start health-check.
Willy Tarreau865c5142016-12-21 20:04:48 +01002080 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002081 */
Willy Tarreau865c5142016-12-21 20:04:48 +01002082static int start_checks()
2083{
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002084
2085 struct proxy *px;
2086 struct server *s;
2087 struct task *t;
Simon Horman4a741432013-02-23 15:35:38 +09002088 int nbcheck=0, mininter=0, srvpos=0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002089
Gaetan Rivet05d692d2020-02-14 17:42:54 +01002090 /* 0- init the dummy frontend used to create all checks sessions */
2091 init_new_proxy(&checks_fe);
2092 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
2093 checks_fe.mode = PR_MODE_TCP;
2094 checks_fe.maxconn = 0;
2095 checks_fe.conn_retries = CONN_RETRIES;
2096 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
2097 checks_fe.timeout.client = TICK_ETERNITY;
2098
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02002099 /* 1- count the checkers to run simultaneously.
2100 * We also determine the minimum interval among all of those which
2101 * have an interval larger than SRV_CHK_INTER_THRES. This interval
2102 * will be used to spread their start-up date. Those which have
Jamie Gloudon801a0a32012-08-25 00:18:33 -04002103 * a shorter interval will start independently and will not dictate
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02002104 * too short an interval for all others.
2105 */
Olivier Houchardfbc74e82017-11-24 16:54:05 +01002106 for (px = proxies_list; px; px = px->next) {
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002107 for (s = px->srv; s; s = s->next) {
Willy Tarreaue7b73482013-11-21 11:50:50 +01002108 if (s->slowstart) {
Emeric Brunc60def82017-09-27 14:59:38 +02002109 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002110 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
Willy Tarreau865c5142016-12-21 20:04:48 +01002111 return ERR_ALERT | ERR_FATAL;
Willy Tarreaue7b73482013-11-21 11:50:50 +01002112 }
2113 /* We need a warmup task that will be called when the server
2114 * state switches from down to up.
2115 */
2116 s->warmup = t;
2117 t->process = server_warmup;
2118 t->context = s;
Baptiste Assmann6076d1c2015-09-17 22:53:59 +02002119 /* server can be in this state only because of */
Emeric Brun52a91d32017-08-31 14:41:55 +02002120 if (s->next_state == SRV_ST_STARTING)
Baptiste Assmann6076d1c2015-09-17 22:53:59 +02002121 task_schedule(s->warmup, tick_add(now_ms, MS_TO_TICKS(MAX(1000, (now.tv_sec - s->last_change)) / 20)));
Willy Tarreaue7b73482013-11-21 11:50:50 +01002122 }
2123
Willy Tarreaud8514a22013-12-11 21:10:14 +01002124 if (s->check.state & CHK_ST_CONFIGURED) {
2125 nbcheck++;
2126 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
2127 (!mininter || mininter > srv_getinter(&s->check)))
2128 mininter = srv_getinter(&s->check);
2129 }
Willy Tarreau15f39102013-12-11 20:41:18 +01002130
Willy Tarreaud8514a22013-12-11 21:10:14 +01002131 if (s->agent.state & CHK_ST_CONFIGURED) {
2132 nbcheck++;
2133 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
2134 (!mininter || mininter > srv_getinter(&s->agent)))
2135 mininter = srv_getinter(&s->agent);
2136 }
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002137 }
2138 }
2139
Simon Horman4a741432013-02-23 15:35:38 +09002140 if (!nbcheck)
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002141 return 0;
2142
2143 srand((unsigned)time(NULL));
2144
2145 /*
2146 * 2- start them as far as possible from each others. For this, we will
2147 * start them after their interval set to the min interval divided by
2148 * the number of servers, weighted by the server's position in the list.
2149 */
Olivier Houchardfbc74e82017-11-24 16:54:05 +01002150 for (px = proxies_list; px; px = px->next) {
Simon Horman98637e52014-06-20 12:30:16 +09002151 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
2152 if (init_pid_list()) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01002153 ha_alert("Starting [%s] check: out of memory.\n", px->id);
Willy Tarreau865c5142016-12-21 20:04:48 +01002154 return ERR_ALERT | ERR_FATAL;
Simon Horman98637e52014-06-20 12:30:16 +09002155 }
2156 }
2157
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002158 for (s = px->srv; s; s = s->next) {
Simon Hormand60d6912013-11-25 10:46:36 +09002159 /* A task for the main check */
Willy Tarreauff5ae352013-12-11 20:36:34 +01002160 if (s->check.state & CHK_ST_CONFIGURED) {
Cyril Bonté99c5bf52014-08-07 01:55:38 +02002161 if (s->check.type == PR_O2_EXT_CHK) {
2162 if (!prepare_external_check(&s->check))
Willy Tarreau865c5142016-12-21 20:04:48 +01002163 return ERR_ALERT | ERR_FATAL;
Cyril Bonté99c5bf52014-08-07 01:55:38 +02002164 }
Simon Hormand60d6912013-11-25 10:46:36 +09002165 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
Willy Tarreau865c5142016-12-21 20:04:48 +01002166 return ERR_ALERT | ERR_FATAL;
Simon Hormand60d6912013-11-25 10:46:36 +09002167 srvpos++;
2168 }
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002169
Simon Hormand60d6912013-11-25 10:46:36 +09002170 /* A task for a auxiliary agent check */
Willy Tarreauff5ae352013-12-11 20:36:34 +01002171 if (s->agent.state & CHK_ST_CONFIGURED) {
Simon Hormand60d6912013-11-25 10:46:36 +09002172 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
Willy Tarreau865c5142016-12-21 20:04:48 +01002173 return ERR_ALERT | ERR_FATAL;
Simon Hormand60d6912013-11-25 10:46:36 +09002174 }
2175 srvpos++;
2176 }
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02002177 }
2178 }
2179 return 0;
2180}
Willy Tarreaubaaee002006-06-26 02:48:02 +02002181
2182/*
Willy Tarreau5b3a2022012-09-28 15:01:02 +02002183 * Perform content verification check on data in s->check.buffer buffer.
Willy Tarreaubd741542010-03-16 18:46:54 +01002184 * The buffer MUST be terminated by a null byte before calling this function.
2185 * Sets server status appropriately. The caller is responsible for ensuring
2186 * that the buffer contains at least 13 characters. If <done> is zero, we may
2187 * return 0 to indicate that data is required to decide of a match.
2188 */
2189static int httpchk_expect(struct server *s, int done)
2190{
Christopher Faulet1bc04c72017-10-29 20:14:08 +01002191 static THREAD_LOCAL char status_msg[] = "HTTP status check returned code <000>";
Willy Tarreaubd741542010-03-16 18:46:54 +01002192 char status_code[] = "000";
2193 char *contentptr;
2194 int crlf;
2195 int ret;
2196
2197 switch (s->proxy->options2 & PR_O2_EXP_TYPE) {
2198 case PR_O2_EXP_STS:
2199 case PR_O2_EXP_RSTS:
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002200 memcpy(status_code, b_head(&s->check.bi) + 9, 3);
2201 memcpy(status_msg + strlen(status_msg) - 4, b_head(&s->check.bi) + 9, 3);
Willy Tarreaubd741542010-03-16 18:46:54 +01002202
2203 if ((s->proxy->options2 & PR_O2_EXP_TYPE) == PR_O2_EXP_STS)
2204 ret = strncmp(s->proxy->expect_str, status_code, 3) == 0;
2205 else
Thierry FOURNIER09af0d62014-06-18 11:35:54 +02002206 ret = regex_exec(s->proxy->expect_regex, status_code);
Willy Tarreaubd741542010-03-16 18:46:54 +01002207
2208 /* we necessarily have the response, so there are no partial failures */
2209 if (s->proxy->options2 & PR_O2_EXP_INV)
2210 ret = !ret;
2211
Simon Horman4a741432013-02-23 15:35:38 +09002212 set_server_check_status(&s->check, ret ? HCHK_STATUS_L7OKD : HCHK_STATUS_L7STS, status_msg);
Willy Tarreaubd741542010-03-16 18:46:54 +01002213 break;
2214
2215 case PR_O2_EXP_STR:
2216 case PR_O2_EXP_RSTR:
2217 /* very simple response parser: ignore CR and only count consecutive LFs,
2218 * stop with contentptr pointing to first char after the double CRLF or
2219 * to '\0' if crlf < 2.
2220 */
2221 crlf = 0;
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002222 for (contentptr = b_head(&s->check.bi); *contentptr; contentptr++) {
Willy Tarreaubd741542010-03-16 18:46:54 +01002223 if (crlf >= 2)
2224 break;
2225 if (*contentptr == '\r')
2226 continue;
2227 else if (*contentptr == '\n')
2228 crlf++;
2229 else
2230 crlf = 0;
2231 }
2232
2233 /* Check that response contains a body... */
2234 if (crlf < 2) {
2235 if (!done)
2236 return 0;
2237
Simon Horman4a741432013-02-23 15:35:38 +09002238 set_server_check_status(&s->check, HCHK_STATUS_L7RSP,
Willy Tarreaubd741542010-03-16 18:46:54 +01002239 "HTTP content check could not find a response body");
2240 return 1;
2241 }
2242
2243 /* Check that response body is not empty... */
2244 if (*contentptr == '\0') {
Willy Tarreaua164fb52011-04-13 09:32:41 +02002245 if (!done)
2246 return 0;
2247
Simon Horman4a741432013-02-23 15:35:38 +09002248 set_server_check_status(&s->check, HCHK_STATUS_L7RSP,
Willy Tarreaubd741542010-03-16 18:46:54 +01002249 "HTTP content check found empty response body");
2250 return 1;
2251 }
2252
2253 /* Check the response content against the supplied string
2254 * or regex... */
2255 if ((s->proxy->options2 & PR_O2_EXP_TYPE) == PR_O2_EXP_STR)
2256 ret = strstr(contentptr, s->proxy->expect_str) != NULL;
2257 else
Thierry FOURNIER09af0d62014-06-18 11:35:54 +02002258 ret = regex_exec(s->proxy->expect_regex, contentptr);
Willy Tarreaubd741542010-03-16 18:46:54 +01002259
2260 /* if we don't match, we may need to wait more */
2261 if (!ret && !done)
2262 return 0;
2263
2264 if (ret) {
2265 /* content matched */
2266 if (s->proxy->options2 & PR_O2_EXP_INV)
Simon Horman4a741432013-02-23 15:35:38 +09002267 set_server_check_status(&s->check, HCHK_STATUS_L7RSP,
Willy Tarreaubd741542010-03-16 18:46:54 +01002268 "HTTP check matched unwanted content");
2269 else
Simon Horman4a741432013-02-23 15:35:38 +09002270 set_server_check_status(&s->check, HCHK_STATUS_L7OKD,
Willy Tarreaubd741542010-03-16 18:46:54 +01002271 "HTTP content check matched");
2272 }
2273 else {
2274 if (s->proxy->options2 & PR_O2_EXP_INV)
Simon Horman4a741432013-02-23 15:35:38 +09002275 set_server_check_status(&s->check, HCHK_STATUS_L7OKD,
Willy Tarreaubd741542010-03-16 18:46:54 +01002276 "HTTP check did not match unwanted content");
2277 else
Simon Horman4a741432013-02-23 15:35:38 +09002278 set_server_check_status(&s->check, HCHK_STATUS_L7RSP,
Willy Tarreaubd741542010-03-16 18:46:54 +01002279 "HTTP content check did not match");
2280 }
2281 break;
2282 }
2283 return 1;
2284}
2285
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002286/*
2287 * return the id of a step in a send/expect session
2288 */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002289static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002290{
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002291 if (!rule)
2292 rule = check->current_step;
Willy Tarreau213c6782014-10-02 14:51:02 +02002293
Christopher Faulet3c29aa62020-03-24 13:31:19 +01002294 /* no last started step => first step */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002295 if (!rule)
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002296 return 1;
2297
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002298 /* last step is the first implicit connect */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002299 if (rule->index == 0 &&
2300 rule->action == TCPCHK_ACT_CONNECT &&
Christopher Fauletbb591a12020-04-01 16:52:17 +02002301 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002302 return 0;
2303
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002304 return rule->index + 1;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002305}
2306
Christopher Faulet206368d2020-04-03 14:51:06 +02002307static void tcpcheck_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
2308 int match, struct ist info)
2309{
2310 struct sample *smp;
2311
2312 if (istlen(info)) {
2313 chunk_strncat(msg, info.ptr, info.len);
2314 goto comment;
2315 }
2316 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
2317 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
2318 goto comment;
2319 }
2320
2321 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
2322 switch (rule->expect.type) {
2323 case TCPCHK_EXPECT_STRING:
2324 chunk_appendf(msg, " '%s' at step %d", rule->expect.string, tcpcheck_get_step_id(check, rule));
2325 break;
2326 case TCPCHK_EXPECT_BINARY:
2327 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
2328 break;
2329 case TCPCHK_EXPECT_REGEX:
2330 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
2331 break;
2332 case TCPCHK_EXPECT_REGEX_BINARY:
2333 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
2334
2335 /* If references to the matched text were made, divide the
2336 * offsets by 2 to match offset of the original response buffer.
2337 */
2338 if (rule->expect.with_capture) {
2339 int i;
2340
2341 for (i = 1; i < MAX_MATCH && pmatch[i].rm_so != -1; i++) {
2342 pmatch[i].rm_so /= 2; /* at first matched char. */
2343 pmatch[i].rm_eo /= 2; /* at last matched char. */
2344 }
2345 }
2346 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +02002347 case TCPCHK_EXPECT_CUSTOM:
2348 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
2349 break;
Christopher Faulet206368d2020-04-03 14:51:06 +02002350 case TCPCHK_EXPECT_UNDEF:
2351 /* Should never happen. */
2352 return;
2353 }
2354
2355 comment:
2356 if (rule->comment) {
2357 chunk_strcat(msg, " comment: ");
2358 if (rule->expect.with_capture) {
2359 int ret = exp_replace(b_tail(msg), b_room(msg), b_head(&check->bi), rule->comment, pmatch);
2360 if (ret != -1) /* ignore comment if too large */
2361 msg->data += ret;
2362 }
2363 else
2364 chunk_strcat(msg, rule->comment);
2365 }
2366
2367 if (rule->expect.status_expr) {
2368 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
2369 rule->expect.status_expr, SMP_T_SINT);
2370 if (smp)
2371 check->code = smp->data.u.sint;
2372 }
2373
2374 *(b_tail(msg)) = '\0';
2375}
2376
2377static void tcpcheck_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
2378 struct ist info)
2379{
2380 struct sample *smp;
2381
2382 if (istlen(info))
2383 chunk_strncat(msg, info.ptr, info.len);
2384 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
2385 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
2386 &rule->expect.onsuccess_fmt);
2387 else
2388 chunk_strcat(msg, "(tcp-check)");
2389
2390 if (rule->expect.status_expr) {
2391 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
2392 rule->expect.status_expr, SMP_T_SINT);
2393 if (smp)
2394 check->code = smp->data.u.sint;
2395 }
2396
2397 *(b_tail(msg)) = '\0';
2398}
2399
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002400static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
2401 unsigned int offset, int last_read)
2402{
2403 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2404 enum healthcheck_status status;
2405 struct buffer *msg = NULL;
2406 struct ist desc = ist(NULL);
2407 unsigned int err = 0, plen = 0;
2408
2409
2410 /* 3 Bytes for the packet length and 1 byte for the sequence id */
2411 if (!last_read && b_data(&check->bi) < offset+4) {
2412 if (!last_read)
2413 goto wait_more_data;
2414
2415 /* invalid length or truncated response */
2416 status = HCHK_STATUS_L7RSP;
2417 goto error;
2418 }
2419
2420 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
2421 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
2422 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
2423
2424 if (b_data(&check->bi) < offset+plen+4) {
2425 if (!last_read)
2426 goto wait_more_data;
2427
2428 /* invalid length or truncated response */
2429 status = HCHK_STATUS_L7RSP;
2430 goto error;
2431 }
2432
2433 if (*b_peek(&check->bi, offset+4) == '\xff') {
2434 /* MySQL Error packet always begin with field_count = 0xff */
2435 status = HCHK_STATUS_L7STS;
2436 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
2437 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
2438 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
2439 goto error;
2440 }
2441
2442 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
2443 /* Not the last rule, continue */
2444 goto out;
2445 }
2446
2447 /* We set the MySQL Version in description for information purpose
2448 * FIXME : it can be cool to use MySQL Version for other purpose,
2449 * like mark as down old MySQL server.
2450 */
2451 set_server_check_status(check, HCHK_STATUS_L7OKD, b_peek(&check->bi, 5));
2452
2453 out:
2454 free_trash_chunk(msg);
2455 return ret;
2456
2457 error:
2458 ret = TCPCHK_EVAL_STOP;
2459 check->code = err;
2460 msg = alloc_trash_chunk();
2461 if (msg)
2462 tcpcheck_onerror_message(msg, check, rule, 0, desc);
2463 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2464 goto out;
2465
2466 wait_more_data:
2467 ret = TCPCHK_EVAL_WAIT;
2468 goto out;
2469}
2470
2471
2472static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
2473{
2474 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
2475}
2476
2477static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
2478{
2479 unsigned int hslen = 0;
2480
2481 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
2482 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
2483 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
2484
2485 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
2486}
2487
Christopher Faulet1997eca2020-04-03 23:13:50 +02002488static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
2489{
2490 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2491 enum healthcheck_status status;
2492 struct buffer *msg = NULL;
2493 struct ist desc = ist(NULL);
2494 unsigned short msglen = 0;
2495
2496 /* Check if the server speaks LDAP (ASN.1/BER)
2497 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
2498 * http://tools.ietf.org/html/rfc4511
2499 */
2500 /* size of LDAPMessage */
2501 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
2502
2503 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
2504 * messageID: 0x02 0x01 0x01: INTEGER 1
2505 * protocolOp: 0x61: bindResponse
2506 */
2507 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
2508 status = HCHK_STATUS_L7RSP;
2509 desc = ist("Not LDAPv3 protocol");
2510 goto error;
2511 }
2512
2513 /* size of bindResponse */
2514 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
2515
2516 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
2517 * ldapResult: 0x0a 0x01: ENUMERATION
2518 */
2519 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
2520 status = HCHK_STATUS_L7RSP;
2521 desc = ist("Not LDAPv3 protocol");
2522 goto error;
2523 }
2524
2525 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
2526 * resultCode
2527 */
2528 check->code = *(b_head(&check->bi) + msglen + 9);
2529 if (check->code) {
2530 status = HCHK_STATUS_L7STS;
2531 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
2532 goto error;
2533 }
2534
2535 set_server_check_status(check, HCHK_STATUS_L7OKD, "Success");
2536
2537 out:
2538 free_trash_chunk(msg);
2539 return ret;
2540
2541 error:
2542 ret = TCPCHK_EVAL_STOP;
2543 msg = alloc_trash_chunk();
2544 if (msg)
2545 tcpcheck_onerror_message(msg, check, rule, 0, desc);
2546 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2547 goto out;
2548
2549 wait_more_data:
2550 ret = TCPCHK_EVAL_WAIT;
2551 goto out;
2552}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002553
Christopher Faulet267b01b2020-04-04 10:27:09 +02002554
2555static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read)
2556{
2557 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2558 enum healthcheck_status status;
2559 struct buffer *msg = NULL;
2560 struct ist desc = ist(NULL);
2561 unsigned int framesz;
2562
2563
2564 memcpy(&framesz, b_head(&check->bi), 4);
2565 framesz = ntohl(framesz);
2566
2567 if (!last_read && b_data(&check->bi) < (4+framesz))
2568 goto wait_more_data;
2569
2570 memset(b_orig(&trash), 0, b_size(&trash));
2571 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
2572 status = HCHK_STATUS_L7RSP;
2573 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
2574 goto error;
2575 }
2576
2577 set_server_check_status(check, HCHK_STATUS_L7OKD, "SPOA server is ok");
2578
2579 out:
2580 free_trash_chunk(msg);
2581 return ret;
2582
2583 error:
2584 ret = TCPCHK_EVAL_STOP;
2585 msg = alloc_trash_chunk();
2586 if (msg)
2587 tcpcheck_onerror_message(msg, check, rule, 0, desc);
2588 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2589 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002590
2591 wait_more_data:
2592 ret = TCPCHK_EVAL_WAIT;
2593 goto out;
2594}
2595
2596static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
2597{
2598 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
2599 enum healthcheck_status status = HCHK_STATUS_CHECKED;
2600 const char *hs = NULL; /* health status */
2601 const char *as = NULL; /* admin status */
2602 const char *ps = NULL; /* performance status */
2603 const char *cs = NULL; /* maxconn */
2604 const char *err = NULL; /* first error to report */
2605 const char *wrn = NULL; /* first warning to report */
2606 char *cmd, *p;
2607
2608 /* We're getting an agent check response. The agent could
2609 * have been disabled in the mean time with a long check
2610 * still pending. It is important that we ignore the whole
2611 * response.
2612 */
2613 if (!(check->state & CHK_ST_ENABLED))
2614 goto out;
2615
2616 /* The agent supports strings made of a single line ended by the
2617 * first CR ('\r') or LF ('\n'). This line is composed of words
2618 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
2619 * line may optionally contained a description of a state change
2620 * after a sharp ('#'), which is only considered if a health state
2621 * is announced.
2622 *
2623 * Words may be composed of :
2624 * - a numeric weight suffixed by the percent character ('%').
2625 * - a health status among "up", "down", "stopped", and "fail".
2626 * - an admin status among "ready", "drain", "maint".
2627 *
2628 * These words may appear in any order. If multiple words of the
2629 * same category appear, the last one wins.
2630 */
2631
2632 p = b_head(&check->bi);
2633 while (*p && *p != '\n' && *p != '\r')
2634 p++;
2635
2636 if (!*p) {
2637 if (!last_read)
2638 goto wait_more_data;
2639
2640 /* at least inform the admin that the agent is mis-behaving */
2641 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
2642 goto out;
2643 }
2644
2645 *p = 0;
2646 cmd = b_head(&check->bi);
2647
2648 while (*cmd) {
2649 /* look for next word */
2650 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
2651 cmd++;
2652 continue;
2653 }
2654
2655 if (*cmd == '#') {
2656 /* this is the beginning of a health status description,
2657 * skip the sharp and blanks.
2658 */
2659 cmd++;
2660 while (*cmd == '\t' || *cmd == ' ')
2661 cmd++;
2662 break;
2663 }
2664
2665 /* find the end of the word so that we have a null-terminated
2666 * word between <cmd> and <p>.
2667 */
2668 p = cmd + 1;
2669 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
2670 p++;
2671 if (*p)
2672 *p++ = 0;
2673
2674 /* first, health statuses */
2675 if (strcasecmp(cmd, "up") == 0) {
2676 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
2677 status = HCHK_STATUS_L7OKD;
2678 hs = cmd;
2679 }
2680 else if (strcasecmp(cmd, "down") == 0) {
2681 check->server->check.health = 0;
2682 status = HCHK_STATUS_L7STS;
2683 hs = cmd;
2684 }
2685 else if (strcasecmp(cmd, "stopped") == 0) {
2686 check->server->check.health = 0;
2687 status = HCHK_STATUS_L7STS;
2688 hs = cmd;
2689 }
2690 else if (strcasecmp(cmd, "fail") == 0) {
2691 check->server->check.health = 0;
2692 status = HCHK_STATUS_L7STS;
2693 hs = cmd;
2694 }
2695 /* admin statuses */
2696 else if (strcasecmp(cmd, "ready") == 0) {
2697 as = cmd;
2698 }
2699 else if (strcasecmp(cmd, "drain") == 0) {
2700 as = cmd;
2701 }
2702 else if (strcasecmp(cmd, "maint") == 0) {
2703 as = cmd;
2704 }
2705 /* try to parse a weight here and keep the last one */
2706 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
2707 ps = cmd;
2708 }
2709 /* try to parse a maxconn here */
2710 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
2711 cs = cmd;
2712 }
2713 else {
2714 /* keep a copy of the first error */
2715 if (!err)
2716 err = cmd;
2717 }
2718 /* skip to next word */
2719 cmd = p;
2720 }
2721 /* here, cmd points either to \0 or to the beginning of a
2722 * description. Skip possible leading spaces.
2723 */
2724 while (*cmd == ' ' || *cmd == '\n')
2725 cmd++;
2726
2727 /* First, update the admin status so that we avoid sending other
2728 * possibly useless warnings and can also update the health if
2729 * present after going back up.
2730 */
2731 if (as) {
2732 if (strcasecmp(as, "drain") == 0)
2733 srv_adm_set_drain(check->server);
2734 else if (strcasecmp(as, "maint") == 0)
2735 srv_adm_set_maint(check->server);
2736 else
2737 srv_adm_set_ready(check->server);
2738 }
2739
2740 /* now change weights */
2741 if (ps) {
2742 const char *msg;
2743
2744 msg = server_parse_weight_change_request(check->server, ps);
2745 if (!wrn || !*wrn)
2746 wrn = msg;
2747 }
2748
2749 if (cs) {
2750 const char *msg;
2751
2752 cs += strlen("maxconn:");
2753
2754 msg = server_parse_maxconn_change_request(check->server, cs);
2755 if (!wrn || !*wrn)
2756 wrn = msg;
2757 }
2758
2759 /* and finally health status */
2760 if (hs) {
2761 /* We'll report some of the warnings and errors we have
2762 * here. Down reports are critical, we leave them untouched.
2763 * Lack of report, or report of 'UP' leaves the room for
2764 * ERR first, then WARN.
2765 */
2766 const char *msg = cmd;
2767 struct buffer *t;
2768
2769 if (!*msg || status == HCHK_STATUS_L7OKD) {
2770 if (err && *err)
2771 msg = err;
2772 else if (wrn && *wrn)
2773 msg = wrn;
2774 }
2775
2776 t = get_trash_chunk();
2777 chunk_printf(t, "via agent : %s%s%s%s",
2778 hs, *msg ? " (" : "",
2779 msg, *msg ? ")" : "");
2780 set_server_check_status(check, status, t->area);
2781 }
2782 else if (err && *err) {
2783 /* No status change but we'd like to report something odd.
2784 * Just report the current state and copy the message.
2785 */
2786 chunk_printf(&trash, "agent reports an error : %s", err);
2787 set_server_check_status(check, status/*check->status*/, trash.area);
2788 }
2789 else if (wrn && *wrn) {
2790 /* No status change but we'd like to report something odd.
2791 * Just report the current state and copy the message.
2792 */
2793 chunk_printf(&trash, "agent warns : %s", wrn);
2794 set_server_check_status(check, status/*check->status*/, trash.area);
2795 }
2796 else
2797 set_server_check_status(check, status, NULL);
2798
2799 out:
2800 return ret;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002801
2802 wait_more_data:
2803 ret = TCPCHK_EVAL_WAIT;
2804 goto out;
2805}
2806
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002807/* Evaluate a TCPCHK_ACT_CONNECT rule. It returns 1 to evaluate the next rule, 0
2808 * to wait and -1 to stop the check. */
2809static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002810{
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002811 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2812 struct tcpcheck_connect *connect = &rule->connect;
Olivier Houchardc98aa1f2019-01-11 18:17:17 +01002813 struct proxy *proxy = check->proxy;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002814 struct server *s = check->server;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002815 struct task *t = check->task;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002816 struct conn_stream *cs;
2817 struct connection *conn = NULL;
2818 struct protocol *proto;
2819 struct xprt_ops *xprt;
Christopher Faulet5c288742020-03-31 08:15:58 +02002820 int status, port;
Willy Tarreauef953952014-10-02 14:30:14 +02002821
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002822 /* For a connect action we'll create a new connection. We may also have
2823 * to kill a previous one. But we don't want to leave *without* a
2824 * connection if we came here from the connection layer, hence with a
2825 * connection. Thus we'll proceed in the following order :
2826 * 1: close but not release previous connection (handled by the caller)
2827 * 2: try to get a new connection
2828 * 3: release and replace the old one on success
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002829 */
Willy Tarreau449f9522015-05-13 15:39:48 +02002830
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002831 /* 2- prepare new connection */
2832 cs = cs_new(NULL);
2833 if (!cs) {
2834 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
2835 tcpcheck_get_step_id(check, rule));
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02002836 if (rule->comment)
2837 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002838 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2839 ret = TCPCHK_EVAL_STOP;
Christopher Fauletb6102852017-11-28 10:06:29 +01002840 goto out;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002841 }
2842
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002843 /* 3- release and replace the old one on success */
2844 if (check->cs) {
2845 if (check->wait_list.events)
2846 cs->conn->xprt->unsubscribe(cs->conn, cs->conn->xprt_ctx,
2847 check->wait_list.events, &check->wait_list);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002848
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002849 /* We may have been scheduled to run, and the I/O handler
2850 * expects to have a cs, so remove the tasklet
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01002851 */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002852 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
2853 cs_destroy(check->cs);
2854 }
Willy Tarreaudeccd112018-06-14 18:38:55 +02002855
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002856 tasklet_set_tid(check->wait_list.tasklet, tid);
Willy Tarreauabca5b62013-12-06 14:19:25 +01002857
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002858 check->cs = cs;
2859 conn = cs->conn;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002860
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002861 /* Maybe there were an older connection we were waiting on */
2862 check->wait_list.events = 0;
2863 conn->target = s ? &s->obj_type : &proxy->obj_type;
Willy Tarreauf3d34822014-12-08 12:11:28 +01002864
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002865 /* no client address */
2866 if (!sockaddr_alloc(&conn->dst)) {
2867 status = SF_ERR_RESOURCE;
2868 goto fail_check;
2869 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002870
Christopher Faulet5c288742020-03-31 08:15:58 +02002871 /* connect to the connect rule addr if specified, otherwise the check
2872 * addr if specified on the server. otherwise, use the server addr
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002873 */
Christopher Faulet5c288742020-03-31 08:15:58 +02002874 *conn->dst = (is_addr(&connect->addr)
2875 ? connect->addr
2876 : (is_addr(&check->addr) ? check->addr : s->addr));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002877 proto = protocol_by_family(conn->dst->ss_family);
Willy Tarreau00149122017-10-04 18:05:01 +02002878
Christopher Faulet5c288742020-03-31 08:15:58 +02002879 port = 0;
2880 if (!port && connect->port)
2881 port = connect->port;
Christopher Fauletb7d30092020-03-30 15:19:03 +02002882 if (!port && connect->port_expr) {
2883 struct sample *smp;
2884
2885 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
2886 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
2887 connect->port_expr, SMP_T_SINT);
2888 if (smp)
2889 port = smp->data.u.sint;
2890 }
Christopher Faulet5c288742020-03-31 08:15:58 +02002891 if (!port && is_inet_addr(&connect->addr))
2892 port = get_host_port(&connect->addr);
2893 if (!port && check->port)
2894 port = check->port;
2895 if (!port && is_inet_addr(&check->addr))
2896 port = get_host_port(&check->addr);
2897 if (!port)
2898 port = s->svc_port;
2899 set_host_port(conn->dst, port);
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002900
Christopher Fauletbb591a12020-04-01 16:52:17 +02002901 xprt = ((connect->options & TCPCHK_OPT_SSL)
2902 ? xprt_get(XPRT_SSL)
2903 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Willy Tarreau00149122017-10-04 18:05:01 +02002904
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002905 conn_prepare(conn, proto, xprt);
Gaetan Rivet05d692d2020-02-14 17:42:54 +01002906 if (conn_install_mux(conn, &mux_pt_ops, cs, proxy, check->sess) < 0) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002907 status = SF_ERR_RESOURCE;
2908 goto fail_check;
2909 }
2910 cs_attach(cs, check, &check_conn_cb);
Willy Tarreau00149122017-10-04 18:05:01 +02002911
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002912 status = SF_ERR_INTERNAL;
2913 if (proto && proto->connect) {
2914 struct tcpcheck_rule *next;
2915 int flags = CONNECT_HAS_DATA;
Olivier Houchardff1e9f32019-09-20 17:18:35 +02002916
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002917 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
2918 if (!next || next->action != TCPCHK_ACT_EXPECT)
2919 flags |= CONNECT_DELACK_ALWAYS;
2920 status = proto->connect(conn, flags);
2921 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002922
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002923#ifdef USE_OPENSSL
Christopher Fauletbb591a12020-04-01 16:52:17 +02002924 if (status == SF_ERR_NONE) {
2925 if (connect->sni)
2926 ssl_sock_set_servername(conn, connect->sni);
2927 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s->check.sni)
2928 ssl_sock_set_servername(conn, s->check.sni);
2929
2930 if (connect->alpn)
2931 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
2932 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s->check.alpn_str)
2933 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002934 }
Christopher Faulet79b31d42020-03-30 13:00:05 +02002935#endif
Christopher Fauletbb591a12020-04-01 16:52:17 +02002936 if ((connect->options & TCPCHK_OPT_SOCKS4) && (s->flags & SRV_F_SOCKS4_PROXY)) {
2937 conn->send_proxy_ofs = 1;
2938 conn->flags |= CO_FL_SOCKS4;
2939 }
2940 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
2941 conn->send_proxy_ofs = 1;
2942 conn->flags |= CO_FL_SOCKS4;
2943 }
2944
2945 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
2946 conn->send_proxy_ofs = 1;
2947 conn->flags |= CO_FL_SEND_PROXY;
2948 }
2949 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
2950 conn->send_proxy_ofs = 1;
2951 conn->flags |= CO_FL_SEND_PROXY;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002952 }
Willy Tarreauca79f592019-07-17 19:04:47 +02002953
Christopher Fauletbb591a12020-04-01 16:52:17 +02002954 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
2955 /* Some servers don't like reset on close */
2956 fdtab[cs->conn->handle.fd].linger_risk = 0;
2957 }
2958
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002959 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
2960 if (xprt_add_hs(conn) < 0)
2961 status = SF_ERR_RESOURCE;
2962 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002963
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002964 fail_check:
2965 /* It can return one of :
2966 * - SF_ERR_NONE if everything's OK
2967 * - SF_ERR_SRVTO if there are no more servers
2968 * - SF_ERR_SRVCL if the connection was refused by the server
2969 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
2970 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
2971 * - SF_ERR_INTERNAL for any other purely internal errors
2972 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
2973 * Note that we try to prevent the network stack from sending the ACK during the
2974 * connect() when a pure TCP check is used (without PROXY protocol).
2975 */
2976 switch (status) {
2977 case SF_ERR_NONE:
2978 /* we allow up to min(inter, timeout.connect) for a connection
2979 * to establish but only when timeout.check is set as it may be
2980 * to short for a full check otherwise
2981 */
2982 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002983
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002984 if (proxy->timeout.check && proxy->timeout.connect) {
2985 int t_con = tick_add(now_ms, proxy->timeout.connect);
2986 t->expire = tick_first(t->expire, t_con);
2987 }
2988 break;
2989 case SF_ERR_SRVTO: /* ETIMEDOUT */
2990 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
2991 chunk_printf(&trash, "TCPCHK error establishing connection at step %d: %s",
2992 tcpcheck_get_step_id(check, rule), strerror(errno));
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02002993 if (rule->comment)
2994 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002995 set_server_check_status(check, HCHK_STATUS_L4CON, trash.area);
2996 ret = TCPCHK_EVAL_STOP;
2997 goto out;
2998 case SF_ERR_PRXCOND:
2999 case SF_ERR_RESOURCE:
3000 case SF_ERR_INTERNAL:
3001 chunk_printf(&trash, "TCPCHK error establishing connection at step %d",
3002 tcpcheck_get_step_id(check, rule));
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02003003 if (rule->comment)
3004 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003005 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
3006 ret = TCPCHK_EVAL_STOP;
3007 goto out;
3008 }
Christopher Fauleta202d1d2020-03-26 17:38:49 +01003009
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003010 /* don't do anything until the connection is established */
3011 if (conn->flags & CO_FL_WAIT_XPRT) {
3012 ret = TCPCHK_EVAL_WAIT;
3013 goto out;
3014 }
Willy Tarreaube373152018-09-06 11:45:30 +02003015
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003016 out:
3017 if (conn && check->result == CHK_RES_FAILED)
3018 conn->flags |= CO_FL_ERROR;
3019 return ret;
3020}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02003021
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003022/* Evaluate a TCPCHK_ACT_SEND rule. It returns 1 to evaluate the next rule, 0
3023 * to wait and -1 to stop the check. */
3024static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
3025{
3026 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
3027 struct tcpcheck_send *send = &rule->send;
3028 struct conn_stream *cs = check->cs;
3029 struct connection *conn = cs_conn(cs);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003030 struct buffer *tmp = NULL;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003031
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003032 /* reset the read & write buffer */
3033 b_reset(&check->bi);
3034 b_reset(&check->bo);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01003035
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003036 switch (send->type) {
3037 case TCPCHK_SEND_STRING:
3038 case TCPCHK_SEND_BINARY:
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003039 if (istlen(send->data) >= b_size(&check->bo)) {
3040 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
3041 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
3042 tcpcheck_get_step_id(check, rule));
3043 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
3044 ret = TCPCHK_EVAL_STOP;
3045 goto out;
3046 }
3047 b_putist(&check->bo, send->data);
3048 break;
3049 case TCPCHK_SEND_STRING_LF:
3050 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
3051 if (!b_data(&check->bo))
3052 goto out;
3053 break;
3054 case TCPCHK_SEND_BINARY_LF:
3055 tmp = alloc_trash_chunk();
3056 if (!tmp)
3057 goto error_lf;
3058 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
3059 if (!b_data(tmp))
3060 goto out;
3061 tmp->area[tmp->data] = '\0';
3062 b_set_data(&check->bo, b_size(&check->bo));
3063 if (parse_binary(b_orig(tmp), &check->bo.area, (int *)&check->bo.data, NULL) == 0)
3064 goto error_lf;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003065 break;
3066 case TCPCHK_SEND_UNDEF:
3067 /* Should never happen. */
3068 ret = TCPCHK_EVAL_STOP;
3069 goto out;
3070 };
Christopher Fauleta202d1d2020-03-26 17:38:49 +01003071
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003072 if (conn->mux->snd_buf(cs, &check->bo, b_data(&check->bo), 0) <= 0) {
3073 ret = TCPCHK_EVAL_WAIT;
3074 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
3075 ret = TCPCHK_EVAL_STOP;
3076 goto out;
3077 }
3078 if (b_data(&check->bo)) {
3079 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
3080 ret = TCPCHK_EVAL_WAIT;
3081 goto out;
3082 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003083
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003084 out:
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003085 free_trash_chunk(tmp);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003086 return ret;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003087
3088 error_lf:
3089 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
3090 tcpcheck_get_step_id(check, rule));
3091 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
3092 ret = TCPCHK_EVAL_STOP;
3093 goto out;
3094
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003095}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003096
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003097/* Evaluate a TCPCHK_ACT_EXPECT rule. It returns 1 to evaluate the next rule, 0
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003098 * to wait and -1 to stop the check.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003099 */
3100static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
3101{
3102 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
3103 struct tcpcheck_expect *expect = &check->current_step->expect;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003104 struct buffer *msg = NULL;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003105 int match;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003106
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003107 /* The current expect might need more data than the previous one, check again
3108 * that the minimum amount data required to match is respected.
3109 */
3110 if (!last_read) {
3111 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
3112 (b_data(&check->bi) < expect->length)) {
3113 ret = TCPCHK_EVAL_WAIT;
3114 goto out;
3115 }
3116 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
3117 ret = TCPCHK_EVAL_WAIT;
3118 goto out;
3119 }
3120 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003121
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003122 /* Make GCC happy ; initialize match to a failure state. */
3123 match = expect->inverse;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003124
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003125 switch (expect->type) {
3126 case TCPCHK_EXPECT_STRING:
3127 case TCPCHK_EXPECT_BINARY:
3128 match = my_memmem(b_head(&check->bi), b_data(&check->bi), expect->string, expect->length) != NULL;
3129 break;
3130 case TCPCHK_EXPECT_REGEX:
3131 if (expect->with_capture)
3132 match = regex_exec_match2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1),
3133 MAX_MATCH, pmatch, 0);
3134 else
3135 match = regex_exec2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1));
3136 break;
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003137
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003138 case TCPCHK_EXPECT_REGEX_BINARY:
3139 chunk_reset(&trash);
3140 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
3141 if (expect->with_capture)
3142 match = regex_exec_match2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1),
3143 MAX_MATCH, pmatch, 0);
3144 else
3145 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
3146 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +02003147 case TCPCHK_EXPECT_CUSTOM:
3148 if (expect->custom)
3149 ret = expect->custom(check, rule, last_read);
3150 goto out;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003151 case TCPCHK_EXPECT_UNDEF:
3152 /* Should never happen. */
3153 ret = TCPCHK_EVAL_STOP;
3154 goto out;
3155 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003156
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003157
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003158 /* Wait for more data on mismatch only if no minimum is defined (-1),
3159 * otherwise the absence of match is already conclusive.
3160 */
3161 if (!match && !last_read && (expect->min_recv == -1)) {
3162 ret = TCPCHK_EVAL_WAIT;
3163 goto out;
3164 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003165
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003166 /* Result as expected, next rule. */
3167 if (match ^ expect->inverse)
3168 goto out;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003169
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003170
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003171 /* From this point on, we matched something we did not want, this is an error state. */
3172 ret = TCPCHK_EVAL_STOP;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003173 msg = alloc_trash_chunk();
Christopher Faulet206368d2020-04-03 14:51:06 +02003174 if (msg)
3175 tcpcheck_onerror_message(msg, check, rule, match, ist(NULL));
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003176 set_server_check_status(check, expect->err_status, (msg ? b_head(msg) : NULL));
Christopher Faulet206368d2020-04-03 14:51:06 +02003177 free_trash_chunk(msg);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003178 ret = TCPCHK_EVAL_STOP;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003179
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003180 out:
3181 return ret;
3182}
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003183
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003184/* Evaluate a TCPCHK_ACT_ACTION_KW rule. It returns 1 to evaluate the next rule, 0
3185 * to wait and -1 to stop the check.
3186 */
3187static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
3188{
3189 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
3190 struct act_rule *act_rule;
3191 enum act_return act_ret;
3192
3193 act_rule =rule->action_kw.rule;
3194 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
3195 if (act_ret != ACT_RET_CONT) {
3196 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
3197 tcpcheck_get_step_id(check, rule));
3198 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
3199 ret = TCPCHK_EVAL_STOP;
3200 }
3201
3202 return ret;
3203}
3204
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003205/* proceed with next steps for the TCP checks <check>. Note that this is called
3206 * both from the connection's wake() callback and from the check scheduling task.
3207 * It returns 0 on normal cases, or <0 if a close() has happened on an existing
3208 * connection, presenting the risk of an fd replacement.
3209 *
3210 * Please do NOT place any return statement in this function and only leave
3211 * via the out_end_tcpcheck label after setting retcode.
3212 */
3213static int tcpcheck_main(struct check *check)
3214{
3215 struct tcpcheck_rule *rule;
3216 struct conn_stream *cs = check->cs;
3217 struct connection *conn = cs_conn(cs);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003218 struct buffer *msg = NULL;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003219 int must_read = 1, last_read = 0;
3220 int ret, retcode = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003221
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003222 /* here, we know that the check is complete or that it failed */
3223 if (check->result != CHK_RES_UNKNOWN)
3224 goto out_end_tcpcheck;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003225
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003226 /* 1- check for connection error, if any */
3227 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
3228 goto out_end_tcpcheck;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003229
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003230 /* 2- check if we are waiting for the connection establishment. It only
3231 * happens during TCPCHK_ACT_CONNECT. */
3232 if (conn && (conn->flags & CO_FL_WAIT_XPRT))
3233 goto out;
Gaetan Rivet9e47fa42020-02-26 15:59:22 +01003234
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003235 /* 3- check for pending outgoing data. It only happens during TCPCHK_ACT_SEND. */
3236 if (conn && b_data(&check->bo)) {
3237 ret = conn->mux->snd_buf(cs, &check->bo, b_data(&check->bo), 0);
3238 if (ret <= 0) {
3239 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
3240 goto out_end_tcpcheck;
3241 goto out;
3242 }
3243 if (b_data(&check->bo)) {
3244 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
3245 goto out;
3246 }
3247 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003248
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003249 /* Now evaluate the tcp-check rules */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003250
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003251 /* If check->current_step is defined, we are in resume condition. For
3252 * TCPCHK_ACT_CONNECT and TCPCHK_ACT_SEND rules, we must go to the next
3253 * rule before resuming the evaluation. For TCPCHK_ACT_EXPECT, we
3254 * re-evaluate the current rule. Others cannot yield.
3255 */
3256 if (check->current_step) {
3257 if (check->current_step->action == TCPCHK_ACT_CONNECT ||
3258 check->current_step->action == TCPCHK_ACT_SEND)
3259 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
3260 else
3261 rule = check->current_step;
3262 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003263 else {
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003264 struct tcpcheck_var *var;
3265
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003266 /* First evaluation, create a session */
Gaetan Rivet13a50432020-02-21 18:13:44 +01003267 check->sess = session_new(&checks_fe, NULL, (check->server ? &check->server->obj_type : NULL));
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003268 if (!check->sess) {
3269 chunk_printf(&trash, "TCPCHK error allocating check session");
3270 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
3271 goto out_end_tcpcheck;
3272 }
Gaetan Rivet13a50432020-02-21 18:13:44 +01003273 vars_init(&check->vars, SCOPE_CHECK);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003274 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003275
3276 /* Preset tcp-check variables */
3277 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
3278 struct sample smp;
3279
3280 memset(&smp, 0, sizeof(smp));
3281 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
3282 smp.data = var->data;
3283 vars_set_by_name_ifexist(var->name.ptr, var->name.len, &smp);
3284 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003285 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003286
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003287 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003288 enum tcpcheck_eval_ret eval_ret;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003289
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003290 switch (rule->action) {
3291 case TCPCHK_ACT_CONNECT:
3292 check->current_step = rule;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003293
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003294 /* close but not release yet previous connection */
3295 if (check->cs) {
3296 cs_close(check->cs);
3297 retcode = -1; /* do not reuse the fd in the caller! */
Gaetan Rivet9e47fa42020-02-26 15:59:22 +01003298 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003299 eval_ret = tcpcheck_eval_connect(check, rule);
3300 must_read = 1; last_read = 0;
3301 break;
3302 case TCPCHK_ACT_SEND:
3303 check->current_step = rule;
3304 eval_ret = tcpcheck_eval_send(check, rule);
3305 must_read = 1;
3306 break;
3307 case TCPCHK_ACT_EXPECT:
3308 check->current_step = rule;
3309 if (must_read) {
3310 if (check->proxy->timeout.check)
3311 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003312
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003313 /* If we already subscribed, then we tried to received and
3314 * failed, so there's no point trying again.
3315 */
3316 if (check->wait_list.events & SUB_RETRY_RECV)
3317 goto out;
3318 if (conn->mux->rcv_buf(cs, &check->bi, b_size(&check->bi), 0) <= 0) {
3319 if (conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH) || cs->flags & CS_FL_ERROR) {
3320 last_read = 1;
3321 if ((conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) && !b_data(&check->bi)) {
3322 /* Report network errors only if we got no other data. Otherwise
3323 * we'll let the upper layers decide whether the response is OK
3324 * or not. It is very common that an RST sent by the server is
3325 * reported as an error just after the last data chunk.
3326 */
3327 goto out_end_tcpcheck;
3328 }
3329 }
3330 else {
3331 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
3332 goto out;
3333 }
3334 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003335
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003336 /* buffer full, don't wait for more data */
3337 if (b_full(&check->bi))
3338 last_read = 1;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003339
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003340 /* Check that response body is not empty... */
3341 if (!b_data(&check->bi)) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003342 if (!last_read)
3343 goto out;
Gaetan Rivet9dcb09f2020-02-07 15:37:17 +01003344
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003345 /* empty response */
3346 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
3347 tcpcheck_get_step_id(check, rule));
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02003348 if (rule->comment)
3349 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02003350 set_server_check_status(check, rule->expect.err_status, trash.area);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003351 ret = -1;
3352 goto out_end_tcpcheck;
Gaetan Rivet9dcb09f2020-02-07 15:37:17 +01003353 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003354 must_read = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003355 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003356
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003357 eval_ret = tcpcheck_eval_expect(check, rule, last_read);
3358 if (eval_ret == TCPCHK_EVAL_WAIT) {
3359 check->current_step = rule->expect.head;
3360 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Gaetan Rivet9dcb09f2020-02-07 15:37:17 +01003361 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003362 break;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003363 case TCPCHK_ACT_ACTION_KW:
3364 /* Don't update the current step */
3365 eval_ret = tcpcheck_eval_action_kw(check, rule);
3366 break;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003367 default:
3368 /* Otherwise, just go to the next one and don't update
3369 * the current step
3370 */
3371 eval_ret = TCPCHK_EVAL_CONTINUE;
3372 break;
3373 }
Baptiste Assmann248f1172018-03-01 21:49:01 +01003374
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003375 switch (eval_ret) {
3376 case TCPCHK_EVAL_CONTINUE:
3377 break;
3378 case TCPCHK_EVAL_WAIT:
3379 goto out;
3380 case TCPCHK_EVAL_STOP:
3381 goto out_end_tcpcheck;
Baptiste Assmann248f1172018-03-01 21:49:01 +01003382 }
Baptiste Assmann248f1172018-03-01 21:49:01 +01003383 }
3384
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003385 /* All rules was evaluated */
Christopher Faulet98cc57c2020-04-01 20:52:31 +02003386 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet206368d2020-04-03 14:51:06 +02003387 msg = alloc_trash_chunk();
3388 if (msg)
3389 tcpcheck_onsuccess_message(msg, check, check->current_step, ist(NULL));
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003390 }
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003391 set_server_check_status(check, HCHK_STATUS_L7OKD, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003392 free_trash_chunk(msg);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003393
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003394 out_end_tcpcheck:
Willy Tarreauef91c932019-07-23 14:37:47 +02003395 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003396 chk_report_conn_err(check, errno, 0);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003397
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003398 /* cleanup before leaving */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003399 check->current_step = NULL;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003400 if (check->sess != NULL) {
Gaetan Rivet13a50432020-02-21 18:13:44 +01003401 vars_prune(&check->vars, check->sess, NULL);
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003402 session_free(check->sess);
3403 check->sess = NULL;
3404 }
3405 out:
3406 return retcode;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003407}
3408
Christopher Faulet31c30fd2020-03-26 21:10:03 +01003409static const char *init_check(struct check *check, int type)
Simon Hormanb1900d52015-01-30 11:22:54 +09003410{
3411 check->type = type;
3412
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003413 b_reset(&check->bi); check->bi.size = global.tune.chksize;
3414 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Simon Hormanb1900d52015-01-30 11:22:54 +09003415
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003416 check->bi.area = calloc(check->bi.size, sizeof(char));
3417 check->bo.area = calloc(check->bo.size, sizeof(char));
3418
3419 if (!check->bi.area || !check->bo.area)
Simon Hormanb1900d52015-01-30 11:22:54 +09003420 return "out of memory while allocating check buffer";
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003421
Willy Tarreau3c39a7d2019-06-14 14:42:29 +02003422 check->wait_list.tasklet = tasklet_new();
3423 if (!check->wait_list.tasklet)
Ilya Shipitsind4259502020-04-08 01:07:56 +05003424 return "out of memory while allocating check tasklet";
Willy Tarreau4f6516d2018-12-19 13:59:17 +01003425 check->wait_list.events = 0;
Willy Tarreau3c39a7d2019-06-14 14:42:29 +02003426 check->wait_list.tasklet->process = event_srv_chk_io;
3427 check->wait_list.tasklet->context = check;
Simon Hormanb1900d52015-01-30 11:22:54 +09003428 return NULL;
3429}
3430
Simon Hormanbfb5d332015-01-30 11:22:55 +09003431void free_check(struct check *check)
3432{
Christopher Faulet8892e5d2020-03-26 19:48:20 +01003433 task_destroy(check->task);
3434 if (check->wait_list.tasklet)
3435 tasklet_free(check->wait_list.tasklet);
3436
Willy Tarreauc9fa0482018-07-10 17:43:27 +02003437 free(check->bi.area);
3438 free(check->bo.area);
Christopher Faulet23d86d12018-01-25 11:36:35 +01003439 if (check->cs) {
3440 free(check->cs->conn);
3441 check->cs->conn = NULL;
3442 cs_free(check->cs);
3443 check->cs = NULL;
3444 }
Simon Hormanbfb5d332015-01-30 11:22:55 +09003445}
3446
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003447static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
3448{
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003449 struct logformat_node *lf, *lfb;
3450
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003451 if (!rule)
3452 return;
3453
3454 free(rule->comment);
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003455 switch (rule->action) {
3456 case TCPCHK_ACT_SEND:
3457 switch (rule->send.type) {
3458 case TCPCHK_SEND_STRING:
3459 case TCPCHK_SEND_BINARY:
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003460 free(rule->send.data.ptr);
3461 break;
3462 case TCPCHK_SEND_STRING_LF:
3463 case TCPCHK_SEND_BINARY_LF:
3464 list_for_each_entry_safe(lf, lfb, &rule->send.fmt, list) {
3465 LIST_DEL(&lf->list);
3466 release_sample_expr(lf->expr);
3467 free(lf->arg);
3468 free(lf);
3469 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003470 break;
3471 case TCPCHK_SEND_UNDEF:
3472 break;
3473 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003474 break;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003475 case TCPCHK_ACT_EXPECT:
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003476 list_for_each_entry_safe(lf, lfb, &rule->expect.onerror_fmt, list) {
3477 LIST_DEL(&lf->list);
3478 release_sample_expr(lf->expr);
3479 free(lf->arg);
3480 free(lf);
3481 }
3482 list_for_each_entry_safe(lf, lfb, &rule->expect.onsuccess_fmt, list) {
3483 LIST_DEL(&lf->list);
3484 release_sample_expr(lf->expr);
3485 free(lf->arg);
3486 free(lf);
3487 }
Christopher Faulet98cc57c2020-04-01 20:52:31 +02003488 release_sample_expr(rule->expect.status_expr);
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003489 switch (rule->expect.type) {
3490 case TCPCHK_EXPECT_STRING:
3491 case TCPCHK_EXPECT_BINARY:
3492 free(rule->expect.string);
3493 break;
3494 case TCPCHK_EXPECT_REGEX:
3495 case TCPCHK_EXPECT_REGEX_BINARY:
3496 regex_free(rule->expect.regex);
3497 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +02003498 case TCPCHK_EXPECT_CUSTOM:
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003499 case TCPCHK_EXPECT_UNDEF:
3500 break;
3501 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003502 break;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003503 case TCPCHK_ACT_CONNECT:
Christopher Faulet79b31d42020-03-30 13:00:05 +02003504 free(rule->connect.sni);
Christopher Faulet98572322020-03-30 13:16:44 +02003505 free(rule->connect.alpn);
Christopher Fauletb7d30092020-03-30 15:19:03 +02003506 release_sample_expr(rule->connect.port_expr);
Christopher Faulet79b31d42020-03-30 13:00:05 +02003507 break;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003508 case TCPCHK_ACT_COMMENT:
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003509 break;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01003510 case TCPCHK_ACT_ACTION_KW:
3511 free(rule->action_kw.rule);
3512 break;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003513 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003514
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003515 if (in_pool)
3516 pool_free(pool_head_tcpcheck_rule, rule);
3517 else
3518 free(rule);
3519}
3520
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003521
Christopher Fauletce355072020-04-02 11:44:39 +02003522static struct tcpcheck_var *tcpcheck_var_create(const char *name)
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003523{
3524 struct tcpcheck_var *var = NULL;
3525
3526 var = calloc(1, sizeof(*var));
3527 if (var == NULL)
3528 return NULL;
3529
3530 var->name = ist2(strdup(name), strlen(name));
3531 if (var->name.ptr == NULL) {
3532 free(var);
3533 return NULL;
3534 }
3535
3536 LIST_INIT(&var->list);
3537 return var;
3538}
3539
3540static void tcpcheck_var_release(struct tcpcheck_var *var)
3541{
3542 if (!var)
3543 return;
3544
3545 free(var->name.ptr);
3546 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
3547 free(var->data.u.str.area);
3548 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
3549 free(var->data.u.meth.str.area);
3550 free(var);
3551}
3552
3553int dup_tcpcheck_vars(struct list *dst, struct list *src)
3554{
3555 struct tcpcheck_var *var, *new = NULL;
3556
3557 list_for_each_entry(var, src, list) {
3558 new = tcpcheck_var_create(var->name.ptr);
3559 if (!new)
3560 goto error;
3561 new->data.type = var->data.type;
3562 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
3563 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
3564 goto error;
3565 if (var->data.type == SMP_T_STR)
3566 new->data.u.str.area[new->data.u.str.data] = 0;
3567 }
3568 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
3569 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
3570 goto error;
3571 new->data.u.str.area[new->data.u.str.data] = 0;
3572 new->data.u.meth.meth = var->data.u.meth.meth;
3573 }
3574 else
3575 new->data.u = var->data.u;
3576 LIST_ADDQ(dst, &new->list);
3577 }
3578 return 1;
3579
3580 error:
3581 free(new);
3582 return 0;
3583}
3584
3585static void free_tcpcheck_vars(struct list *vars)
3586{
3587 struct tcpcheck_var *var, *back;
3588
3589 list_for_each_entry_safe(var, back, vars, list) {
3590 LIST_DEL(&var->list);
3591 tcpcheck_var_release(var);
3592 }
3593}
3594
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003595void email_alert_free(struct email_alert *alert)
3596{
3597 struct tcpcheck_rule *rule, *back;
3598
3599 if (!alert)
3600 return;
3601
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003602 if (alert->rules.list) {
3603 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
3604 LIST_DEL(&rule->list);
3605 free_tcpcheck(rule, 1);
3606 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003607 free_tcpcheck_vars(&alert->rules.preset_vars);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003608 free(alert->rules.list);
3609 alert->rules.list = NULL;
Christopher Fauletde1a75b2017-10-23 15:38:19 +02003610 }
Willy Tarreaubafbe012017-11-24 17:34:44 +01003611 pool_free(pool_head_email_alert, alert);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003612}
3613
Olivier Houchard9f6af332018-05-25 14:04:04 +02003614static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003615{
Olivier Houchard9f6af332018-05-25 14:04:04 +02003616 struct check *check = context;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003617 struct email_alertq *q;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003618 struct email_alert *alert;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003619
3620 q = container_of(check, typeof(*q), check);
3621
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003622 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0108bb32017-10-20 21:34:32 +02003623 while (1) {
3624 if (!(check->state & CHK_ST_ENABLED)) {
3625 if (LIST_ISEMPTY(&q->email_alerts)) {
3626 /* All alerts processed, queue the task */
3627 t->expire = TICK_ETERNITY;
3628 task_queue(t);
Christopher Fauletc2a89a62017-10-23 15:54:24 +02003629 goto end;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003630 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003631
3632 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003633 LIST_DEL(&alert->list);
Christopher Faulet0108bb32017-10-20 21:34:32 +02003634 t->expire = now_ms;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003635 check->tcpcheck_rules = &alert->rules;
Olivier Houchard0923fa42019-01-11 18:43:04 +01003636 check->status = HCHK_STATUS_INI;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003637 check->state |= CHK_ST_ENABLED;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003638 }
3639
Olivier Houchard9f6af332018-05-25 14:04:04 +02003640 process_chk(t, context, state);
Christopher Faulet0108bb32017-10-20 21:34:32 +02003641 if (check->state & CHK_ST_INPROGRESS)
3642 break;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003643
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003644 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003645 email_alert_free(alert);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003646 check->tcpcheck_rules = NULL;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003647 check->server = NULL;
3648 check->state &= ~CHK_ST_ENABLED;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003649 }
Christopher Fauletc2a89a62017-10-23 15:54:24 +02003650 end:
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003651 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003652 return t;
3653}
3654
Christopher Faulet0108bb32017-10-20 21:34:32 +02003655/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
3656 *
3657 * The function returns 1 in success case, otherwise, it returns 0 and err is
3658 * filled.
3659 */
3660int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003661{
Christopher Faulet0108bb32017-10-20 21:34:32 +02003662 struct mailer *mailer;
3663 struct email_alertq *queues;
3664 const char *err_str;
3665 int i = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003666
Christopher Faulet0108bb32017-10-20 21:34:32 +02003667 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
3668 memprintf(err, "out of memory while allocating mailer alerts queues");
mildis5ab01cb2018-10-02 16:46:34 +02003669 goto fail_no_queue;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003670 }
3671
Christopher Faulet0108bb32017-10-20 21:34:32 +02003672 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
3673 struct email_alertq *q = &queues[i];
3674 struct check *check = &q->check;
3675 struct task *t;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003676
3677 LIST_INIT(&q->email_alerts);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003678 HA_SPIN_INIT(&q->lock);
Christopher Faulet0108bb32017-10-20 21:34:32 +02003679 check->inter = mls->timeout.mail;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003680 check->rise = DEF_AGENT_RISETIME;
Olivier Houchardc98aa1f2019-01-11 18:17:17 +01003681 check->proxy = p;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003682 check->fall = DEF_AGENT_FALLTIME;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003683 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
3684 memprintf(err, "%s", err_str);
3685 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003686 }
3687
3688 check->xprt = mailer->xprt;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003689 check->addr = mailer->addr;
Christopher Fauletb797ae12018-03-27 15:35:35 +02003690 check->port = get_host_port(&mailer->addr);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003691
Emeric Brunc60def82017-09-27 14:59:38 +02003692 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
Christopher Faulet0108bb32017-10-20 21:34:32 +02003693 memprintf(err, "out of memory while allocating mailer alerts task");
3694 goto error;
3695 }
3696
3697 check->task = t;
3698 t->process = process_email_alert;
3699 t->context = check;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003700
Christopher Faulet0108bb32017-10-20 21:34:32 +02003701 /* check this in one ms */
3702 t->expire = TICK_ETERNITY;
3703 check->start = now;
3704 task_queue(t);
3705 }
3706
3707 mls->users++;
3708 free(p->email_alert.mailers.name);
3709 p->email_alert.mailers.m = mls;
3710 p->email_alert.queues = queues;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003711 return 0;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003712
3713 error:
3714 for (i = 0; i < mls->count; i++) {
3715 struct email_alertq *q = &queues[i];
3716 struct check *check = &q->check;
3717
Christopher Faulet0108bb32017-10-20 21:34:32 +02003718 free_check(check);
3719 }
3720 free(queues);
mildis5ab01cb2018-10-02 16:46:34 +02003721 fail_no_queue:
Christopher Faulet0108bb32017-10-20 21:34:32 +02003722 return 1;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003723}
3724
3725
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003726static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003727{
Gaetan Rivet4038b942020-02-26 16:19:40 +01003728 struct tcpcheck_rule *tcpcheck, *prev_check;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003729 struct tcpcheck_expect *expect;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003730
Willy Tarreaubafbe012017-11-24 17:34:44 +01003731 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003732 return 0;
Christopher Faulet31dff9b2017-10-23 15:45:20 +02003733 memset(tcpcheck, 0, sizeof(*tcpcheck));
Gaetan Rivetb616add2020-02-07 15:37:17 +01003734 tcpcheck->action = TCPCHK_ACT_EXPECT;
3735
3736 expect = &tcpcheck->expect;
3737 expect->type = TCPCHK_EXPECT_STRING;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003738 LIST_INIT(&expect->onerror_fmt);
3739 LIST_INIT(&expect->onsuccess_fmt);
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02003740 expect->err_status = HCHK_STATUS_L7RSP;
3741 expect->tout_status = HCHK_STATUS_L7TOUT;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003742 expect->string = strdup(str);
3743 if (!expect->string) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01003744 pool_free(pool_head_tcpcheck_rule, tcpcheck);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003745 return 0;
3746 }
Gaetan Rivetb616add2020-02-07 15:37:17 +01003747 expect->length = strlen(expect->string);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003748
Gaetan Rivet4038b942020-02-26 16:19:40 +01003749 /* All tcp-check expect points back to the first inverse expect rule
3750 * in a chain of one or more expect rule, potentially itself.
3751 */
Gaetan Rivetb616add2020-02-07 15:37:17 +01003752 tcpcheck->expect.head = tcpcheck;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003753 list_for_each_entry_rev(prev_check, rules->list, list) {
Gaetan Rivet4038b942020-02-26 16:19:40 +01003754 if (prev_check->action == TCPCHK_ACT_EXPECT) {
Gaetan Rivetb616add2020-02-07 15:37:17 +01003755 if (prev_check->expect.inverse)
3756 tcpcheck->expect.head = prev_check;
Gaetan Rivet4038b942020-02-26 16:19:40 +01003757 continue;
3758 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003759 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
Gaetan Rivet4038b942020-02-26 16:19:40 +01003760 break;
3761 }
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003762 LIST_ADDQ(rules->list, &tcpcheck->list);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003763 return 1;
3764}
3765
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003766static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003767{
3768 struct tcpcheck_rule *tcpcheck;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003769 struct tcpcheck_send *send;
Willy Tarreau64345aa2016-08-10 19:29:09 +02003770 const char *in;
3771 char *dst;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003772 int i;
3773
Willy Tarreaubafbe012017-11-24 17:34:44 +01003774 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003775 return 0;
Christopher Faulet31dff9b2017-10-23 15:45:20 +02003776 memset(tcpcheck, 0, sizeof(*tcpcheck));
3777 tcpcheck->action = TCPCHK_ACT_SEND;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003778
3779 send = &tcpcheck->send;
3780 send->type = TCPCHK_SEND_STRING;
3781
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003782 for (i = 0; strs[i]; i++)
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003783 send->data.len += strlen(strs[i]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003784
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003785 send->data.ptr = malloc(send->data.len + 1);
3786 if (!isttest(send->data)) {
Willy Tarreaubafbe012017-11-24 17:34:44 +01003787 pool_free(pool_head_tcpcheck_rule, tcpcheck);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003788 return 0;
3789 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003790
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003791 dst = send->data.ptr;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003792 for (i = 0; strs[i]; i++)
Willy Tarreau64345aa2016-08-10 19:29:09 +02003793 for (in = strs[i]; (*dst = *in++); dst++);
3794 *dst = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003795
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003796 LIST_ADDQ(rules->list, &tcpcheck->list);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003797 return 1;
3798}
3799
Christopher Faulet0108bb32017-10-20 21:34:32 +02003800static int enqueue_one_email_alert(struct proxy *p, struct server *s,
3801 struct email_alertq *q, const char *msg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003802{
Christopher Faulet31dff9b2017-10-23 15:45:20 +02003803 struct email_alert *alert;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003804 struct tcpcheck_rule *tcpcheck;
3805 struct check *check = &q->check;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003806
Willy Tarreaubafbe012017-11-24 17:34:44 +01003807 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003808 goto error;
Christopher Faulet31dff9b2017-10-23 15:45:20 +02003809 LIST_INIT(&alert->list);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003810 alert->rules.flags = 0;
3811 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
3812 if (!alert->rules.list)
3813 goto error;
3814 LIST_INIT(alert->rules.list);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003815 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
Christopher Faulet0108bb32017-10-20 21:34:32 +02003816 alert->srv = s;
Christopher Faulet31dff9b2017-10-23 15:45:20 +02003817
Willy Tarreaubafbe012017-11-24 17:34:44 +01003818 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003819 goto error;
Christopher Faulet31dff9b2017-10-23 15:45:20 +02003820 memset(tcpcheck, 0, sizeof(*tcpcheck));
3821 tcpcheck->action = TCPCHK_ACT_CONNECT;
3822 tcpcheck->comment = NULL;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003823
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003824 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003825
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003826 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003827 goto error;
3828
3829 {
3830 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003831 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003832 goto error;
3833 }
3834
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003835 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003836 goto error;
3837
3838 {
3839 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003840 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003841 goto error;
3842 }
3843
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003844 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003845 goto error;
3846
3847 {
3848 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003849 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003850 goto error;
3851 }
3852
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003853 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003854 goto error;
3855
3856 {
3857 const char * const strs[2] = { "DATA\r\n" };
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003858 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003859 goto error;
3860 }
3861
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003862 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003863 goto error;
3864
3865 {
3866 struct tm tm;
3867 char datestr[48];
3868 const char * const strs[18] = {
Pieter Baauw5e0964e2016-02-13 16:27:35 +01003869 "From: ", p->email_alert.from, "\r\n",
3870 "To: ", p->email_alert.to, "\r\n",
3871 "Date: ", datestr, "\r\n",
3872 "Subject: [HAproxy Alert] ", msg, "\r\n",
3873 "\r\n",
3874 msg, "\r\n",
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003875 "\r\n",
Pieter Baauwed35c372015-07-22 19:51:54 +02003876 ".\r\n",
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003877 NULL
3878 };
3879
3880 get_localtime(date.tv_sec, &tm);
3881
3882 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
3883 goto error;
3884 }
3885
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003886 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003887 goto error;
3888 }
3889
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003890 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003891 goto error;
3892
3893 {
3894 const char * const strs[2] = { "QUIT\r\n" };
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003895 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003896 goto error;
3897 }
3898
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003899 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003900 goto error;
3901
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003902 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0108bb32017-10-20 21:34:32 +02003903 task_wakeup(check->task, TASK_WOKEN_MSG);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003904 LIST_ADDQ(&q->email_alerts, &alert->list);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01003905 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003906 return 1;
3907
3908error:
3909 email_alert_free(alert);
3910 return 0;
3911}
3912
Christopher Faulet0108bb32017-10-20 21:34:32 +02003913static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003914{
3915 int i;
3916 struct mailer *mailer;
3917
3918 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
3919 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
Christopher Faulet0108bb32017-10-20 21:34:32 +02003920 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003921 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003922 return;
3923 }
3924 }
3925
3926 return;
3927}
3928
3929/*
3930 * Send email alert if configured.
3931 */
Simon Horman64e34162015-02-06 11:11:57 +09003932void send_email_alert(struct server *s, int level, const char *format, ...)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003933{
3934 va_list argp;
3935 char buf[1024];
3936 int len;
3937 struct proxy *p = s->proxy;
3938
Christopher Faulet0108bb32017-10-20 21:34:32 +02003939 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003940 return;
3941
3942 va_start(argp, format);
3943 len = vsnprintf(buf, sizeof(buf), format, argp);
3944 va_end(argp);
3945
Thierry FOURNIER62c8a212017-02-09 12:19:27 +01003946 if (len < 0 || len >= sizeof(buf)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003947 ha_alert("Email alert [%s] could not format message\n", p->id);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003948 return;
3949 }
3950
Christopher Faulet0108bb32017-10-20 21:34:32 +02003951 enqueue_email_alert(p, s, buf);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003952}
3953
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003954/*
3955 * Return value:
3956 * the port to be used for the health check
3957 * 0 in case no port could be found for the check
3958 */
Christopher Faulet31c30fd2020-03-26 21:10:03 +01003959static int srv_check_healthcheck_port(struct check *chk)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003960{
3961 int i = 0;
3962 struct server *srv = NULL;
3963
3964 srv = chk->server;
3965
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003966 /* by default, we use the health check port ocnfigured */
3967 if (chk->port > 0)
3968 return chk->port;
3969
3970 /* try to get the port from check_core.addr if check.port not set */
3971 i = get_host_port(&chk->addr);
3972 if (i > 0)
3973 return i;
3974
3975 /* try to get the port from server address */
3976 /* prevent MAPPORTS from working at this point, since checks could
3977 * not be performed in such case (MAPPORTS impose a relative ports
3978 * based on live traffic)
3979 */
3980 if (srv->flags & SRV_F_MAPPORTS)
3981 return 0;
Willy Tarreau04276f32017-01-06 17:41:29 +01003982
3983 i = srv->svc_port; /* by default */
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02003984 if (i > 0)
3985 return i;
3986
3987 return 0;
3988}
3989
Willy Tarreau172f5ce2018-11-26 11:21:50 +01003990REGISTER_POST_CHECK(start_checks);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003991
Christopher Fauleta202d1d2020-03-26 17:38:49 +01003992static int check_proxy_tcpcheck(struct proxy *px)
3993{
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02003994 struct tcpcheck_rule *chk, *back;
3995 char *comment = NULL;
3996 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01003997 int ret = 0;
3998
Christopher Faulet5d503fc2020-03-30 20:34:34 +02003999 if ((px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004000 goto out;
4001
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004002 if (!px->tcpcheck_rules.list) {
4003 px->tcpcheck_rules.list = calloc(1, sizeof(*px->tcpcheck_rules.list));
4004 if (!px->tcpcheck_rules.list) {
4005 ha_alert("config : proxy '%s': out of memory.\n", px->id);
4006 ret |= ERR_ALERT | ERR_FATAL;
4007 goto out;
4008 }
4009 LIST_INIT(px->tcpcheck_rules.list);
4010 }
4011
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004012 /* If there is no connect rule preceeding all send / expect rules, an
4013 * implicit one is inserted before all others
4014 */
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004015 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004016 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
4017 chk = calloc(1, sizeof(*chk));
4018 if (!chk) {
4019 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
4020 "(out of memory).\n", px->id);
4021 ret |= ERR_ALERT | ERR_FATAL;
4022 goto out;
4023 }
4024 chk->action = TCPCHK_ACT_CONNECT;
Christopher Fauletbb591a12020-04-01 16:52:17 +02004025 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004026 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004027 }
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004028
4029 /* Now remove comment rules */
4030 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
4031 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
4032 free(comment);
4033 comment = NULL;
4034 }
4035
4036 prev_action = chk->action;
4037 switch (chk->action) {
4038 case TCPCHK_ACT_COMMENT:
4039 free(comment);
4040 comment = chk->comment;
4041 LIST_DEL(&chk->list);
4042 free(chk);
4043 break;
4044 case TCPCHK_ACT_CONNECT:
4045 if (!chk->comment && comment)
4046 chk->comment = strdup(comment);
4047 /* fall though */
4048 case TCPCHK_ACT_ACTION_KW:
4049 free(comment);
4050 comment = NULL;
4051 break;
4052 case TCPCHK_ACT_SEND:
4053 case TCPCHK_ACT_EXPECT:
4054 if (!chk->comment && comment)
4055 chk->comment = strdup(comment);
4056 break;
4057 }
4058 }
4059 free(comment);
4060 comment = NULL;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004061
4062 out:
4063 return ret;
4064}
4065
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004066static int init_srv_check(struct server *srv)
4067{
4068 const char *err;
4069 struct tcpcheck_rule *r;
4070 int ret = 0;
4071
4072 if (!srv->do_check)
4073 goto out;
4074
4075
4076 /* If neither a port nor an addr was specified and no check transport
4077 * layer is forced, then the transport layer used by the checks is the
4078 * same as for the production traffic. Otherwise we use raw_sock by
4079 * default, unless one is specified.
4080 */
4081 if (!srv->check.port && !is_addr(&srv->check.addr)) {
4082 if (!srv->check.use_ssl && srv->use_ssl != -1) {
4083 srv->check.use_ssl = srv->use_ssl;
4084 srv->check.xprt = srv->xprt;
4085 }
4086 else if (srv->check.use_ssl == 1)
4087 srv->check.xprt = xprt_get(XPRT_SSL);
4088
4089 srv->check.send_proxy |= (srv->pp_opts);
4090 }
4091
4092 /* validate <srv> server health-check settings */
4093
4094 /* We need at least a service port, a check port or the first tcp-check
4095 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
4096 */
4097 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
4098 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
4099 goto init;
4100
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004101 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004102 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
4103 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4104 ret |= ERR_ALERT | ERR_ABORT;
4105 goto out;
4106 }
4107
4108 /* search the first action (connect / send / expect) in the list */
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004109 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
Christopher Faulet5c288742020-03-31 08:15:58 +02004110 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004111 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
4112 "nor tcp_check rule 'connect' with port information.\n",
4113 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4114 ret |= ERR_ALERT | ERR_ABORT;
4115 goto out;
4116 }
4117
4118 /* scan the tcp-check ruleset to ensure a port has been configured */
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004119 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
Christopher Faulet5c288742020-03-31 08:15:58 +02004120 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004121 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
4122 "and a tcp_check rule 'connect' with no port information.\n",
4123 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4124 ret |= ERR_ALERT | ERR_ABORT;
4125 goto out;
4126 }
4127 }
4128
4129 init:
4130 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
4131 if (err) {
4132 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
4133 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
4134 ret |= ERR_ALERT | ERR_ABORT;
4135 goto out;
4136 }
4137 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
4138 global.maxsock++;
4139
4140 out:
4141 return ret;
4142}
4143
4144static int init_srv_agent_check(struct server *srv)
4145{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004146 struct tcpcheck_rule *chk;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004147 const char *err;
4148 int ret = 0;
4149
4150 if (!srv->do_agent)
4151 goto out;
4152
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004153 /* If there is no connect rule preceeding all send / expect rules, an
4154 * implicit one is inserted before all others.
4155 */
4156 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
4157 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
4158 chk = calloc(1, sizeof(*chk));
4159 if (!chk) {
4160 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
4161 " to agent-check for server '%s' (out of memory).\n",
4162 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
4163 ret |= ERR_ALERT | ERR_FATAL;
4164 goto out;
4165 }
4166 chk->action = TCPCHK_ACT_CONNECT;
4167 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
4168 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
4169 }
4170
4171
4172 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004173 if (err) {
4174 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
4175 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
4176 ret |= ERR_ALERT | ERR_ABORT;
4177 goto out;
4178 }
4179
4180 if (!srv->agent.inter)
4181 srv->agent.inter = srv->check.inter;
4182
4183 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
4184 global.maxsock++;
4185
4186 out:
4187 return ret;
4188}
4189
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004190void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004191{
4192 struct tcpcheck_rule *chk, *back;
4193
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004194 if (!px->tcpcheck_rules.list || (px->tcpcheck_rules.flags & TCPCHK_RULES_SHARED))
4195 goto end;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004196
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004197 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004198 LIST_DEL(&chk->list);
4199 free_tcpcheck(chk, 0);
4200 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02004201 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004202 free(px->tcpcheck_rules.list);
4203
4204 end:
4205 px->tcpcheck_rules.flags = 0;
4206 px->tcpcheck_rules.list = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004207}
4208
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004209static void deinit_srv_check(struct server *srv)
4210{
Christopher Fauletce8111e2020-04-06 15:04:11 +02004211 if (srv->check.state & CHK_ST_CONFIGURED)
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004212 free_check(&srv->check);
Christopher Fauletce8111e2020-04-06 15:04:11 +02004213 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
4214 srv->do_check = 0;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004215}
4216
4217
4218static void deinit_srv_agent_check(struct server *srv)
4219{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004220 if (srv->agent.tcpcheck_rules) {
4221 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
4222 free(srv->agent.tcpcheck_rules);
4223 srv->agent.tcpcheck_rules = NULL;
4224 }
4225
4226 if (srv->agent.state & CHK_ST_CONFIGURED)
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004227 free_check(&srv->agent);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004228
4229 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
4230 srv->do_agent = 0;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004231}
4232
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004233static void deinit_tcpchecks()
4234{
4235 struct tcpcheck_ruleset *rs, *rsb;
4236 struct tcpcheck_rule *r, *rb;
4237
4238 list_for_each_entry_safe(rs, rsb, &tcpchecks_list, list) {
4239 LIST_DEL(&rs->list);
4240 list_for_each_entry_safe(r, rb, &rs->rules, list) {
4241 LIST_DEL(&r->list);
4242 free_tcpcheck(r, 0);
4243 }
4244 free(rs->name);
4245 free(rs);
4246 }
4247}
4248
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004249
4250REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004251REGISTER_POST_SERVER_CHECK(init_srv_check);
4252REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
4253
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004254REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004255REGISTER_SERVER_DEINIT(deinit_srv_check);
4256REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004257REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004258
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004259/* extracts check payload at a fixed position and length */
4260static int
4261smp_fetch_chk_payload(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
4262{
4263 unsigned int buf_offset = ((arg_p[0].type == ARGT_SINT) ? arg_p[0].data.sint : 0);
4264 unsigned int buf_size = ((arg_p[1].type == ARGT_SINT) ? arg_p[1].data.sint : 0);
4265 struct server *srv = (smp->sess ? objt_server(smp->sess->origin) : NULL);
4266 struct buffer *buf;
4267
4268 if (!srv || !srv->do_check)
4269 return 0;
4270
4271 buf = &srv->check.bi;
4272 if (buf_offset > b_data(buf))
4273 goto no_match;
4274 if (buf_offset + buf_size > b_data(buf))
4275 buf_size = 0;
4276
4277 /* init chunk as read only */
4278 smp->data.type = SMP_T_STR;
4279 smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
4280 chunk_initlen(&smp->data.u.str, b_head(buf) + buf_offset, 0, (buf_size ? buf_size : (b_data(buf) - buf_offset)));
4281
4282 return 1;
4283
4284 no_match:
4285 smp->flags = 0;
4286 return 0;
4287}
4288
4289static struct sample_fetch_kw_list smp_kws = {ILH, {
4290 { "check.payload", smp_fetch_chk_payload, ARG2(0,SINT,SINT), NULL, SMP_T_STR, SMP_USE_INTRN },
4291 { /* END */ },
4292}};
4293
4294INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
4295
4296
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004297struct action_kw_list tcp_check_keywords = {
4298 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
4299};
4300
4301/* Return the struct action_kw associated to a keyword */
4302static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
4303{
4304 return action_lookup(&tcp_check_keywords.list, kw);
4305}
4306
4307static void action_kw_tcp_check_build_list(struct buffer *chk)
4308{
4309 action_build_list(&tcp_check_keywords.list, chk);
4310}
4311
4312/* Create a tcp-check rule resulting from parsing a custom keyword. */
4313static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004314 struct list *rules, struct action_kw *kw,
4315 const char *file, int line, char **errmsg)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004316{
4317 struct tcpcheck_rule *chk = NULL;
4318 struct act_rule *actrule = NULL;
4319
4320 actrule = calloc(1, sizeof(*actrule));
4321 if (!actrule) {
4322 memprintf(errmsg, "out of memory");
4323 goto error;
4324 }
4325 actrule->kw = kw;
4326 actrule->from = ACT_F_TCP_CHK;
4327
4328 cur_arg++;
4329 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
4330 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
4331 goto error;
4332 }
4333
4334 chk = calloc(1, sizeof(*chk));
4335 if (!chk) {
4336 memprintf(errmsg, "out of memory");
4337 goto error;
4338 }
4339 chk->action = TCPCHK_ACT_ACTION_KW;
4340 chk->action_kw.rule = actrule;
4341 return chk;
4342
4343 error:
4344 free(actrule);
4345 return NULL;
4346}
4347
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004348static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
Christopher Fauletb7d30092020-03-30 15:19:03 +02004349 const char *file, int line, char **errmsg)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004350{
4351 struct tcpcheck_rule *chk = NULL;
Christopher Faulet5c288742020-03-31 08:15:58 +02004352 struct sockaddr_storage *sk = NULL;
Christopher Faulet98572322020-03-30 13:16:44 +02004353 char *comment = NULL, *sni = NULL, *alpn = NULL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02004354 struct sample_expr *port_expr = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004355 unsigned short conn_opts = 0;
4356 long port = 0;
Christopher Faulet98572322020-03-30 13:16:44 +02004357 int alpn_len = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004358
4359 list_for_each_entry(chk, rules, list) {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004360 if (chk->action != TCPCHK_ACT_COMMENT && chk->action != TCPCHK_ACT_ACTION_KW)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004361 break;
4362 }
4363 if (&chk->list != rules && chk->action != TCPCHK_ACT_CONNECT) {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004364 memprintf(errmsg, "first step MUST also be a 'connect', "
4365 "optionnaly preceded by a 'set-var', an 'unset-var' or a 'comment', "
4366 "when there is a 'connect' step in the tcp-check ruleset");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004367 goto error;
4368 }
4369
4370 cur_arg++;
4371 while (*(args[cur_arg])) {
Christopher Fauletbb591a12020-04-01 16:52:17 +02004372 if (strcmp(args[cur_arg], "default") == 0)
4373 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
Christopher Faulet5c288742020-03-31 08:15:58 +02004374 else if (strcmp(args[cur_arg], "addr") == 0) {
4375 int port1, port2;
4376 struct protocol *proto;
4377
4378 if (!*(args[cur_arg+1])) {
4379 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
4380 goto error;
4381 }
4382
4383 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
4384 if (!sk) {
4385 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
4386 goto error;
4387 }
4388
4389 proto = protocol_by_family(sk->ss_family);
4390 if (!proto || !proto->connect) {
4391 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
4392 args[cur_arg]);
4393 goto error;
4394 }
4395
4396 if (port1 != port2) {
4397 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
4398 args[cur_arg], args[cur_arg+1]);
4399 goto error;
4400 }
4401
4402 cur_arg++;
4403 }
Christopher Faulet4dce5922020-03-30 13:54:42 +02004404 else if (strcmp(args[cur_arg], "port") == 0) {
Christopher Fauletb7d30092020-03-30 15:19:03 +02004405 const char *p, *end;
4406
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004407 if (!*(args[cur_arg+1])) {
Christopher Fauletb7d30092020-03-30 15:19:03 +02004408 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004409 goto error;
4410 }
4411 cur_arg++;
Christopher Fauletb7d30092020-03-30 15:19:03 +02004412
4413 port = 0;
4414 release_sample_expr(port_expr);
4415 p = args[cur_arg]; end = p + strlen(p);
4416 port = read_uint(&p, end);
4417 if (p != end) {
4418 int idx = 0;
4419
4420 px->conf.args.ctx = ARGC_SRV;
4421 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4422 file, line, errmsg, &px->conf.args, NULL);
4423
4424 if (!port_expr) {
4425 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
4426 goto error;
4427 }
4428 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4429 memprintf(errmsg, "error detected while parsing port expression : "
4430 " fetch method '%s' extracts information from '%s', "
4431 "none of which is available here.\n",
4432 args[cur_arg], sample_src_names(port_expr->fetch->use));
4433 goto error;
4434 }
4435 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
4436 }
4437 else if (port > 65535 || port < 1) {
4438 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
4439 args[cur_arg]);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004440 goto error;
4441 }
4442 }
4443 else if (strcmp(args[cur_arg], "comment") == 0) {
4444 if (!*(args[cur_arg+1])) {
4445 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4446 goto error;
4447 }
4448 cur_arg++;
4449 free(comment);
4450 comment = strdup(args[cur_arg]);
4451 if (!comment) {
4452 memprintf(errmsg, "out of memory");
4453 goto error;
4454 }
4455 }
4456 else if (strcmp(args[cur_arg], "send-proxy") == 0)
4457 conn_opts |= TCPCHK_OPT_SEND_PROXY;
Christopher Faulet085426a2020-03-30 13:07:02 +02004458 else if (strcmp(args[cur_arg], "via-socks4") == 0)
4459 conn_opts |= TCPCHK_OPT_SOCKS4;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004460 else if (strcmp(args[cur_arg], "linger") == 0)
4461 conn_opts |= TCPCHK_OPT_LINGER;
4462#ifdef USE_OPENSSL
4463 else if (strcmp(args[cur_arg], "ssl") == 0) {
4464 px->options |= PR_O_TCPCHK_SSL;
4465 conn_opts |= TCPCHK_OPT_SSL;
4466 }
Christopher Faulet79b31d42020-03-30 13:00:05 +02004467 else if (strcmp(args[cur_arg], "sni") == 0) {
4468 if (!*(args[cur_arg+1])) {
4469 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4470 goto error;
4471 }
4472 cur_arg++;
4473 free(sni);
4474 sni = strdup(args[cur_arg]);
4475 if (!sni) {
4476 memprintf(errmsg, "out of memory");
4477 goto error;
4478 }
4479 }
Christopher Faulet98572322020-03-30 13:16:44 +02004480 else if (strcmp(args[cur_arg], "alpn") == 0) {
4481#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
4482 free(alpn);
4483 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
4484 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
4485 goto error;
4486 }
4487 cur_arg++;
4488#else
4489 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
4490 goto error;
4491#endif
4492 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004493#endif /* USE_OPENSSL */
4494
4495 else {
Christopher Faulet5c288742020-03-31 08:15:58 +02004496 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004497#ifdef USE_OPENSSL
Christopher Faulet98572322020-03-30 13:16:44 +02004498 ", 'ssl', 'sni', 'alpn'"
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004499#endif /* USE_OPENSSL */
Christopher Faulet4dce5922020-03-30 13:54:42 +02004500 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004501 args[cur_arg]);
4502 goto error;
4503 }
4504 cur_arg++;
4505 }
4506
4507 chk = calloc(1, sizeof(*chk));
4508 if (!chk) {
4509 memprintf(errmsg, "out of memory");
4510 goto error;
4511 }
Gaetan Rivet06d963a2020-02-21 18:49:05 +01004512 chk->action = TCPCHK_ACT_CONNECT;
4513 chk->comment = comment;
4514 chk->connect.port = port;
4515 chk->connect.options = conn_opts;
Christopher Faulet79b31d42020-03-30 13:00:05 +02004516 chk->connect.sni = sni;
Christopher Faulet98572322020-03-30 13:16:44 +02004517 chk->connect.alpn = alpn;
4518 chk->connect.alpn_len= alpn_len;
Christopher Fauletb7d30092020-03-30 15:19:03 +02004519 chk->connect.port_expr= port_expr;
Christopher Faulet5c288742020-03-31 08:15:58 +02004520 if (sk)
4521 chk->connect.addr = *sk;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004522 return chk;
4523
4524 error:
Christopher Faulet98572322020-03-30 13:16:44 +02004525 free(alpn);
Christopher Faulet79b31d42020-03-30 13:00:05 +02004526 free(sni);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004527 free(comment);
Christopher Fauletb7d30092020-03-30 15:19:03 +02004528 release_sample_expr(port_expr);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004529 return NULL;
4530}
4531
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004532static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004533 const char *file, int line, char **errmsg)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004534{
4535 struct tcpcheck_rule *chk = NULL;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004536 char *comment = NULL, *data = NULL;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01004537 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004538
Gaetan Rivet48219dc2020-02-21 18:41:28 +01004539 type = ((strcmp(args[cur_arg], "send-binary") == 0) ? TCPCHK_SEND_BINARY : TCPCHK_SEND_STRING);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004540 if (!*(args[cur_arg+1])) {
Gaetan Rivet48219dc2020-02-21 18:41:28 +01004541 memprintf(errmsg, "'%s' expects a %s as argument",
4542 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004543 goto error;
4544 }
4545
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004546 data = args[cur_arg+1];
4547
4548 cur_arg += 2;
4549 while (*(args[cur_arg])) {
4550 if (strcmp(args[cur_arg], "comment") == 0) {
4551 if (!*(args[cur_arg+1])) {
4552 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4553 goto error;
4554 }
4555 cur_arg++;
4556 free(comment);
4557 comment = strdup(args[cur_arg]);
4558 if (!comment) {
4559 memprintf(errmsg, "out of memory");
4560 goto error;
4561 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004562 }
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004563 else if (strcmp(args[cur_arg], "log-format") == 0) {
4564 if (type == TCPCHK_SEND_BINARY)
4565 type = TCPCHK_SEND_BINARY_LF;
4566 else if (type == TCPCHK_SEND_STRING)
4567 type = TCPCHK_SEND_STRING_LF;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004568 }
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004569 else {
4570 memprintf(errmsg, "expects 'comment', 'log-format' but got '%s' as argument.",
4571 args[cur_arg]);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004572 goto error;
4573 }
4574 cur_arg++;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004575 }
4576
4577 chk = calloc(1, sizeof(*chk));
4578 if (!chk) {
4579 memprintf(errmsg, "out of memory");
4580 goto error;
4581 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01004582 chk->action = TCPCHK_ACT_SEND;
4583 chk->comment = comment;
4584 chk->send.type = type;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004585
4586 switch (chk->send.type) {
4587 case TCPCHK_SEND_STRING:
4588 chk->send.data = ist2(strdup(data), strlen(data));
4589 if (!isttest(chk->send.data)) {
4590 memprintf(errmsg, "out of memory");
4591 goto error;
4592 }
4593 break;
4594 case TCPCHK_SEND_BINARY:
4595 if (parse_binary(data, &chk->send.data.ptr, (int *)&chk->send.data.len, errmsg) == 0) {
4596 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
4597 goto error;
4598 }
4599 break;
4600 case TCPCHK_SEND_STRING_LF:
4601 case TCPCHK_SEND_BINARY_LF:
4602 LIST_INIT(&chk->send.fmt);
4603 px->conf.args.ctx = ARGC_SRV;
4604 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4605 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
4606 goto error;
4607 }
4608 break;
4609 case TCPCHK_SEND_UNDEF:
4610 goto error;
4611 }
4612
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004613 return chk;
4614
4615 error:
Christopher Fauletf50f4e92020-03-30 19:52:29 +02004616 free(chk);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004617 free(comment);
4618 return NULL;
4619}
4620
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004621static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4622 const char *file, int line, char **errmsg)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004623{
4624 struct tcpcheck_rule *chk = NULL;
4625 char *comment = NULL;
4626
4627 if (!*(args[cur_arg+1])) {
4628 memprintf(errmsg, "expects a string as argument");
4629 goto error;
4630 }
4631 cur_arg++;
4632 comment = strdup(args[cur_arg]);
4633 if (!comment) {
4634 memprintf(errmsg, "out of memory");
4635 goto error;
4636 }
4637
4638 chk = calloc(1, sizeof(*chk));
4639 if (!chk) {
4640 memprintf(errmsg, "out of memory");
4641 goto error;
4642 }
4643 chk->action = TCPCHK_ACT_COMMENT;
4644 chk->comment = comment;
4645 return chk;
4646
4647 error:
4648 free(comment);
4649 return NULL;
4650}
4651
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004652static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px, struct list *rules,
4653 const char *file, int line, char **errmsg)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004654{
4655 struct tcpcheck_rule *prev_check, *chk = NULL;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02004656 struct sample_expr *status_expr = NULL;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02004657 char *str, *on_success_msg, *on_error_msg, *comment, *pattern;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004658 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02004659 enum healthcheck_status err_st = HCHK_STATUS_L7RSP;
4660 enum healthcheck_status tout_st = HCHK_STATUS_L7TOUT;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004661 long min_recv = -1;
4662 int inverse = 0, with_capture = 0;
4663
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02004664 str = on_success_msg = on_error_msg = comment = pattern = NULL;
Christopher Faulet9e6ed152020-04-03 15:24:06 +02004665 if (!*(args[cur_arg+1])) {
4666 memprintf(errmsg, "expects at least a matching pattern as arguments");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004667 goto error;
4668 }
4669
4670 cur_arg++;
4671 while (*(args[cur_arg])) {
4672 int in_pattern = 0;
4673
4674 rescan:
4675 if (strcmp(args[cur_arg], "min-recv") == 0) {
4676 if (in_pattern) {
4677 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4678 goto error;
4679 }
4680 if (!*(args[cur_arg+1])) {
4681 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4682 goto error;
4683 }
4684 /* Use an signed integer here because of chksize */
4685 cur_arg++;
4686 min_recv = atol(args[cur_arg]);
4687 if (min_recv < -1 || min_recv > INT_MAX) {
4688 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4689 goto error;
4690 }
4691 }
4692 else if (*(args[cur_arg]) == '!') {
4693 in_pattern = 1;
4694 while (*(args[cur_arg]) == '!') {
4695 inverse = !inverse;
4696 args[cur_arg]++;
4697 }
4698 if (!*(args[cur_arg]))
4699 cur_arg++;
4700 goto rescan;
4701 }
4702 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "binary") == 0 ||
4703 strcmp(args[cur_arg], "rstring") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4704 if (type != TCPCHK_EXPECT_UNDEF) {
4705 memprintf(errmsg, "only on pattern expected");
4706 goto error;
4707 }
4708 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING :
4709 ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY :
4710 ((*(args[cur_arg]+1) == 's') ? TCPCHK_EXPECT_REGEX : TCPCHK_EXPECT_REGEX_BINARY)));
4711
4712 if (!*(args[cur_arg+1])) {
4713 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4714 goto error;
4715 }
4716 cur_arg++;
4717 pattern = args[cur_arg];
4718 }
Christopher Faulet9e6ed152020-04-03 15:24:06 +02004719 else if (strcmp(args[cur_arg], "custom") == 0) {
4720 if (in_pattern) {
4721 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4722 goto error;
4723 }
4724 if (type != TCPCHK_EXPECT_UNDEF) {
4725 memprintf(errmsg, "only on pattern expected");
4726 goto error;
4727 }
4728 type = TCPCHK_EXPECT_CUSTOM;
4729 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004730 else if (strcmp(args[cur_arg], "comment") == 0) {
4731 if (in_pattern) {
4732 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4733 goto error;
4734 }
4735 if (!*(args[cur_arg+1])) {
4736 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4737 goto error;
4738 }
4739 cur_arg++;
4740 free(comment);
4741 comment = strdup(args[cur_arg]);
4742 if (!comment) {
4743 memprintf(errmsg, "out of memory");
4744 goto error;
4745 }
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02004746 }
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02004747 else if (strcmp(args[cur_arg], "on-success") == 0) {
4748 if (in_pattern) {
4749 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4750 goto error;
4751 }
4752 if (!*(args[cur_arg+1])) {
4753 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4754 goto error;
4755 }
4756 cur_arg++;
4757 free(on_success_msg);
4758 on_success_msg = strdup(args[cur_arg]);
4759 if (!on_success_msg) {
4760 memprintf(errmsg, "out of memory");
4761 goto error;
4762 }
4763 }
4764 else if (strcmp(args[cur_arg], "on-error") == 0) {
4765 if (in_pattern) {
4766 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4767 goto error;
4768 }
4769 if (!*(args[cur_arg+1])) {
4770 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4771 goto error;
4772 }
4773 cur_arg++;
4774 free(on_error_msg);
4775 on_error_msg = strdup(args[cur_arg]);
4776 if (!on_error_msg) {
4777 memprintf(errmsg, "out of memory");
4778 goto error;
4779 }
4780 }
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02004781 else if (strcmp(args[cur_arg], "error-status") == 0) {
4782 if (in_pattern) {
4783 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4784 goto error;
4785 }
4786 if (!*(args[cur_arg+1])) {
4787 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4788 goto error;
4789 }
4790 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4791 err_st = HCHK_STATUS_L7RSP;
4792 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4793 err_st = HCHK_STATUS_L7STS;
4794 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4795 err_st = HCHK_STATUS_L6RSP;
4796 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4797 err_st = HCHK_STATUS_L4CON;
4798 else {
4799 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4800 args[cur_arg], args[cur_arg+1]);
4801 goto error;
4802 }
Christopher Faulet98cc57c2020-04-01 20:52:31 +02004803 cur_arg++;
4804 }
4805 else if (strcmp(args[cur_arg], "status-code") == 0) {
4806 int idx = 0;
4807
4808 if (in_pattern) {
4809 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4810 goto error;
4811 }
4812 if (!*(args[cur_arg+1])) {
4813 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4814 goto error;
4815 }
4816
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02004817 cur_arg++;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02004818 release_sample_expr(status_expr);
4819 px->conf.args.ctx = ARGC_SRV;
4820 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4821 file, line, errmsg, &px->conf.args, NULL);
4822 if (!status_expr) {
4823 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4824 goto error;
4825 }
4826 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4827 memprintf(errmsg, "error detected while parsing status-code expression : "
4828 " fetch method '%s' extracts information from '%s', "
4829 "none of which is available here.\n",
4830 args[cur_arg], sample_src_names(status_expr->fetch->use));
4831 goto error;
4832 }
4833 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02004834 }
4835 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4836 if (in_pattern) {
4837 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4838 goto error;
4839 }
4840 if (!*(args[cur_arg+1])) {
4841 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4842 goto error;
4843 }
4844 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4845 tout_st = HCHK_STATUS_L7TOUT;
4846 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4847 tout_st = HCHK_STATUS_L6TOUT;
4848 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4849 tout_st = HCHK_STATUS_L4TOUT;
4850 else {
4851 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4852 args[cur_arg], args[cur_arg+1]);
4853 goto error;
4854 }
4855 cur_arg++;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004856 }
4857 else {
4858 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]rbinary'"
4859 " or comment but got '%s' as argument.", args[cur_arg]);
4860 goto error;
4861 }
4862
4863 cur_arg++;
4864 }
4865
4866 if (comment) {
4867 char *p = comment;
4868
4869 while (*p) {
4870 if (*p == '\\') {
4871 p++;
4872 if (!*p || !isdigit((unsigned char)*p) ||
4873 (*p == 'x' && (!*(p+1) || !*(p+2) || !ishex(*(p+1)) || !ishex(*(p+2))))) {
4874 memprintf(errmsg, "invalid backreference in 'comment' argument");
4875 goto error;
4876 }
4877 with_capture = 1;
4878 }
4879 p++;
4880 }
4881 if (with_capture && !inverse)
4882 memprintf(errmsg, "using backreference in a positive expect comment is useless");
4883 }
4884
4885 chk = calloc(1, sizeof(*chk));
4886 if (!chk) {
4887 memprintf(errmsg, "out of memory");
4888 goto error;
4889 }
4890 chk->action = TCPCHK_ACT_EXPECT;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02004891 LIST_INIT(&chk->expect.onerror_fmt);
4892 LIST_INIT(&chk->expect.onsuccess_fmt);
4893 chk->comment = comment; comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004894 chk->expect.type = type;
4895 chk->expect.min_recv = min_recv;
4896 chk->expect.inverse = inverse;
4897 chk->expect.with_capture = with_capture;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02004898 chk->expect.err_status = err_st;
4899 chk->expect.tout_status = tout_st;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02004900 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004901
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02004902 if (on_success_msg) {
4903 px->conf.args.ctx = ARGC_SRV;
4904 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4905 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4906 goto error;
4907 }
4908 free(on_success_msg);
4909 on_success_msg = NULL;
4910 }
4911 if (on_error_msg) {
4912 px->conf.args.ctx = ARGC_SRV;
4913 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4914 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4915 goto error;
4916 }
4917 free(on_error_msg);
4918 on_error_msg = NULL;
4919 }
4920
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004921 switch (chk->expect.type) {
4922 case TCPCHK_EXPECT_STRING:
4923 chk->expect.string = strdup(pattern);
4924 chk->expect.length = strlen(pattern);
4925 if (!chk->expect.string) {
4926 memprintf(errmsg, "out of memory");
4927 goto error;
4928 }
4929 break;
4930 case TCPCHK_EXPECT_BINARY:
4931 if (parse_binary(pattern, &chk->expect.string, &chk->expect.length, errmsg) == 0) {
4932 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4933 goto error;
4934 }
4935 case TCPCHK_EXPECT_REGEX:
4936 case TCPCHK_EXPECT_REGEX_BINARY:
4937 chk->expect.regex = regex_comp(pattern, 1, with_capture, errmsg);
4938 if (!chk->expect.regex)
4939 goto error;
4940 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +02004941 case TCPCHK_EXPECT_CUSTOM:
4942 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4943 break;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004944 case TCPCHK_EXPECT_UNDEF:
4945 free(chk);
4946 memprintf(errmsg, "pattern not found");
4947 goto error;
4948 }
4949
4950 /* All tcp-check expect points back to the first inverse expect rule in
4951 * a chain of one or more expect rule, potentially itself.
4952 */
4953 chk->expect.head = chk;
4954 list_for_each_entry_rev(prev_check, rules, list) {
4955 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4956 if (prev_check->expect.inverse)
4957 chk->expect.head = prev_check;
4958 continue;
4959 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01004960 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004961 break;
4962 }
4963 return chk;
4964
4965 error:
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02004966 free_tcpcheck(chk, 0);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004967 free(str);
4968 free(comment);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02004969 free(on_success_msg);
4970 free(on_error_msg);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02004971 release_sample_expr(status_expr);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004972 return NULL;
4973}
4974
4975/* Parses the "tcp-check" proxy keyword */
4976static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
4977 struct proxy *defpx, const char *file, int line,
4978 char **errmsg)
4979{
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004980 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004981 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01004982 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01004983
4984 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
4985 ret = 1;
4986
4987 if (curpx == defpx) {
4988 memprintf(errmsg, "'%s' not allowed in 'defaults' section.", args[0]);
4989 goto error;
4990 }
4991
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004992 if (rules->flags & TCPCHK_RULES_DEF) {
4993 /* Only shared ruleset can be inherited from the default section */
4994 rules->flags = 0;
4995 rules->list = NULL;
4996 }
4997 if (rules->list && (rules->flags & TCPCHK_RULES_SHARED)) {
4998 memprintf(errmsg, "%s : A shared tcp-check ruleset already configured.", args[0]);
4999 goto error;
5000 }
5001
5002 if (!rules->list) {
5003 rules->list = calloc(1, sizeof(*rules->list));
5004 if (!rules->list) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005005 memprintf(errmsg, "%s : out of memory.", args[0]);
5006 goto error;
5007 }
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005008 LIST_INIT(rules->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005009 }
5010
Gaetan Rivet5301b012020-02-25 17:19:17 +01005011 index = 0;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005012 if (!LIST_ISEMPTY(rules->list)) {
5013 chk = LIST_PREV(rules->list, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005014 index = chk->index + 1;
5015 }
5016
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005017 cur_arg = 1;
5018 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005019 chk = parse_tcpcheck_connect(args, cur_arg, curpx, rules->list, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005020 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0)
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005021 chk = parse_tcpcheck_send(args, cur_arg, curpx, rules->list, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005022 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005023 chk = parse_tcpcheck_expect(args, cur_arg, curpx, rules->list, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005024 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005025 chk = parse_tcpcheck_comment(args, cur_arg, curpx, rules->list, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005026 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005027 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5028
5029 if (!kw) {
5030 action_kw_tcp_check_build_list(&trash);
5031 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5032 "%s%s. but got '%s'",
5033 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5034 goto error;
5035 }
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005036 chk = parse_tcpcheck_action(args, cur_arg, curpx, rules->list, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005037 }
5038
5039 if (!chk) {
5040 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5041 goto error;
5042 }
5043 ret = (*errmsg != NULL); /* Handle warning */
5044
5045 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005046 chk->index = index;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005047 LIST_ADDQ(rules->list, &chk->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005048 return ret;
5049
5050 error:
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005051 deinit_proxy_tcpcheck(curpx);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005052 return -1;
5053}
5054
Christopher Faulet33f05df2020-04-01 11:08:50 +02005055
5056static struct tcpcheck_ruleset *tcpcheck_ruleset_lookup(const char *name)
5057{
5058 struct tcpcheck_ruleset *rs;
5059
5060 list_for_each_entry(rs, &tcpchecks_list, list) {
5061 if (strcmp(rs->name, name) == 0)
5062 return rs;
5063 }
5064 return NULL;
5065}
5066
5067static struct tcpcheck_ruleset *tcpcheck_ruleset_create(const char *name)
5068{
5069 struct tcpcheck_ruleset *rs;
5070
5071 rs = calloc(1, sizeof(*rs));
5072 if (rs == NULL)
5073 return NULL;
5074
5075 rs->name = strdup(name);
5076 if (rs->name == NULL) {
5077 free(rs);
5078 return NULL;
5079 }
5080
5081 LIST_INIT(&rs->list);
5082 LIST_INIT(&rs->rules);
5083 LIST_ADDQ(&tcpchecks_list, &rs->list);
5084 return rs;
5085}
5086
5087static void tcpcheck_ruleset_release(struct tcpcheck_ruleset *rs)
5088{
5089 struct tcpcheck_rule *r, *rb;
5090 if (!rs)
5091 return;
5092
5093 LIST_DEL(&rs->list);
5094 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5095 LIST_DEL(&r->list);
5096 free_tcpcheck(r, 0);
5097 }
5098 free(rs->name);
5099 free(rs);
5100}
5101
5102
5103/* Parses the "option redis-check" proxy keyword */
5104int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5105 const char *file, int line)
5106{
5107 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
5108 static char *redis_res = "+PONG\r\n";
5109
5110 struct tcpcheck_ruleset *rs = NULL;
5111 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5112 struct tcpcheck_rule *chk;
5113 char *errmsg = NULL;
5114 int err_code = 0;
5115
5116 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5117 err_code |= ERR_WARN;
5118
5119 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
5120 goto out;
5121
5122 if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
5123 ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
5124 file, line);
5125 err_code |= ERR_ALERT | ERR_FATAL;
5126 goto out;
5127 }
5128
5129 curpx->options2 &= ~PR_O2_CHK_ANY;
5130 curpx->options2 |= PR_O2_TCPCHK_CHK;
5131
5132 free_tcpcheck_vars(&rules->preset_vars);
5133 rules->list = NULL;
5134 rules->flags = 0;
5135
5136 rs = tcpcheck_ruleset_lookup("*redis-check");
5137 if (rs)
5138 goto ruleset_found;
5139
5140 rs = tcpcheck_ruleset_create("*redis-check");
5141 if (rs == NULL) {
5142 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5143 goto error;
5144 }
5145
5146 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
5147 1, curpx, &rs->rules, file, line, &errmsg);
5148 if (!chk) {
5149 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5150 goto error;
5151 }
5152 chk->index = 0;
5153 LIST_ADDQ(&rs->rules, &chk->list);
5154
5155 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
5156 "error-status", "L7STS",
5157 "on-error", "%[check.payload(),cut_crlf]",
5158 "on-success", "Redis server is ok",
5159 ""},
5160 1, curpx, &rs->rules, file, line, &errmsg);
5161 if (!chk) {
5162 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5163 goto error;
5164 }
5165 chk->index = 1;
5166 LIST_ADDQ(&rs->rules, &chk->list);
5167
5168 LIST_ADDQ(&tcpchecks_list, &rs->list);
5169
5170 ruleset_found:
5171 rules->list = &rs->rules;
5172 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_REDIS_CHK);
5173
5174 out:
5175 free(errmsg);
5176 return err_code;
5177
5178 error:
5179 tcpcheck_ruleset_release(rs);
5180 err_code |= ERR_ALERT | ERR_FATAL;
5181 goto out;
5182}
5183
Christopher Faulet811f78c2020-04-01 11:10:27 +02005184
5185/* Parses the "option ssl-hello-chk" proxy keyword */
5186int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5187 const char *file, int line)
5188{
5189 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
5190 * ssl-hello-chk option to ensure that the remote server speaks SSL.
5191 *
5192 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
5193 */
5194 static char sslv3_client_hello[] = {
5195 "16" /* ContentType : 0x16 = Hanshake */
5196 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
5197 "0079" /* ContentLength : 0x79 bytes after this one */
5198 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
5199 "000075" /* HandshakeLength : 0x75 bytes after this one */
5200 "0300" /* Hello Version : 0x0300 = v3 */
5201 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
5202 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
5203 "00" /* Session ID length : empty (no session ID) */
5204 "004E" /* Cipher Suite Length : 78 bytes after this one */
5205 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
5206 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
5207 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
5208 "000D" "000E" "000F" "0010" /* various bit lengths, */
5209 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
5210 "0015" "0016" "0017" "0018"
5211 "0019" "001A" "001B" "002F"
5212 "0030" "0031" "0032" "0033"
5213 "0034" "0035" "0036" "0037"
5214 "0038" "0039" "003A"
5215 "01" /* Compression Length : 0x01 = 1 byte for types */
5216 "00" /* Compression Type : 0x00 = NULL compression */
5217 };
5218
5219 struct tcpcheck_ruleset *rs = NULL;
5220 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5221 struct tcpcheck_rule *chk;
5222 char *errmsg = NULL;
5223 int err_code = 0;
5224
5225 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5226 err_code |= ERR_WARN;
5227
5228 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
5229 goto out;
5230
5231 if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
5232 ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
5233 file, line);
5234 err_code |= ERR_ALERT | ERR_FATAL;
5235 goto out;
5236 }
5237
5238 curpx->options2 &= ~PR_O2_CHK_ANY;
5239 curpx->options2 |= PR_O2_TCPCHK_CHK;
5240
5241 free_tcpcheck_vars(&rules->preset_vars);
5242 rules->list = NULL;
5243 rules->flags = 0;
5244
5245 rs = tcpcheck_ruleset_lookup("*ssl-hello-check");
5246 if (rs)
5247 goto ruleset_found;
5248
5249 rs = tcpcheck_ruleset_create("*ssl-hello-check");
5250 if (rs == NULL) {
5251 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5252 goto error;
5253 }
5254
5255 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", sslv3_client_hello, "log-format", ""},
5256 1, curpx, &rs->rules, file, line, &errmsg);
5257 if (!chk) {
5258 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5259 goto error;
5260 }
5261 chk->index = 0;
5262 LIST_ADDQ(&rs->rules, &chk->list);
5263
5264 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
5265 "min-recv", "5",
5266 "error-status", "L6RSP", "tout-status", "L6TOUT",
5267 ""},
5268 1, curpx, &rs->rules, file, line, &errmsg);
5269 if (!chk) {
5270 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5271 goto error;
5272 }
5273 chk->index = 1;
5274 LIST_ADDQ(&rs->rules, &chk->list);
5275
5276 LIST_ADDQ(&tcpchecks_list, &rs->list);
5277
5278 ruleset_found:
5279 rules->list = &rs->rules;
5280 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_SSL3_CHK);
5281
5282 out:
5283 free(errmsg);
5284 return err_code;
5285
5286 error:
5287 tcpcheck_ruleset_release(rs);
5288 err_code |= ERR_ALERT | ERR_FATAL;
5289 goto out;
5290}
5291
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02005292/* Parses the "option smtpchk" proxy keyword */
5293int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5294 const char *file, int line)
5295{
5296 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
5297
5298 struct tcpcheck_ruleset *rs = NULL;
5299 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5300 struct tcpcheck_rule *chk;
5301 struct tcpcheck_var *var = NULL;
5302 char *cmd = NULL, *errmsg = NULL;
5303 int err_code = 0;
5304
5305 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5306 err_code |= ERR_WARN;
5307
5308 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
5309 goto out;
5310
5311 if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
5312 ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
5313 file, line);
5314 err_code |= ERR_ALERT | ERR_FATAL;
5315 goto out;
5316 }
5317
5318 curpx->options2 &= ~PR_O2_CHK_ANY;
5319 curpx->options2 |= PR_O2_TCPCHK_CHK;
5320
5321 free_tcpcheck_vars(&rules->preset_vars);
5322 rules->list = NULL;
5323 rules->flags = 0;
5324
5325 cur_arg += 2;
5326 if (*args[cur_arg] && *args[cur_arg+1] &&
5327 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
5328 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
5329 if (cmd)
5330 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
5331 }
5332 else {
5333 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
5334 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
5335 cmd = strdup("HELO localhost");
5336 }
5337
5338 var = tcpcheck_var_create("check.smtp_cmd");
5339 if (cmd == NULL || var == NULL) {
5340 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5341 goto error;
5342 }
5343 var->data.type = SMP_T_STR;
5344 var->data.u.str.area = cmd;
5345 var->data.u.str.data = strlen(cmd);
5346 LIST_INIT(&var->list);
5347 LIST_ADDQ(&rules->preset_vars, &var->list);
5348 cmd = NULL;
5349 var = NULL;
5350
5351 rs = tcpcheck_ruleset_lookup("*smtp-check");
5352 if (rs)
5353 goto ruleset_found;
5354
5355 rs = tcpcheck_ruleset_create("*smtp-check");
5356 if (rs == NULL) {
5357 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5358 goto error;
5359 }
5360
5361 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
5362 1, curpx, &rs->rules, file, line, &errmsg);
5363 if (!chk) {
5364 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5365 goto error;
5366 }
5367 chk->index = 0;
5368 LIST_ADDQ(&rs->rules, &chk->list);
5369
5370 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
5371 "min-recv", "4",
5372 "error-status", "L7RSP",
5373 "on-error", "%[check.payload(),cut_crlf]",
5374 ""},
5375 1, curpx, &rs->rules, file, line, &errmsg);
5376 if (!chk) {
5377 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5378 goto error;
5379 }
5380 chk->index = 1;
5381 LIST_ADDQ(&rs->rules, &chk->list);
5382
5383 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
5384 "min-recv", "4",
5385 "error-status", "L7STS",
5386 "on-error", "%[check.payload(4,0),ltrim(' '),cut_crlf]",
5387 "status-code", "check.payload(0,3)",
5388 ""},
5389 1, curpx, &rs->rules, file, line, &errmsg);
5390 if (!chk) {
5391 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5392 goto error;
5393 }
5394 chk->index = 2;
5395 LIST_ADDQ(&rs->rules, &chk->list);
5396
5397 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", smtp_req, "log-format", ""},
5398 1, curpx, &rs->rules, file, line, &errmsg);
5399 if (!chk) {
5400 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5401 goto error;
5402 }
5403 chk->index = 3;
5404 LIST_ADDQ(&rs->rules, &chk->list);
5405
5406 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
5407 "min-recv", "4",
5408 "error-status", "L7STS",
5409 "on-error", "%[check.payload(4,0),ltrim(' '),cut_crlf]",
5410 "on-success", "%[check.payload(4,0),ltrim(' '),cut_crlf]",
5411 "status-code", "check.payload(0,3)",
5412 ""},
5413 1, curpx, &rs->rules, file, line, &errmsg);
5414 if (!chk) {
5415 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5416 goto error;
5417 }
5418 chk->index = 4;
5419 LIST_ADDQ(&rs->rules, &chk->list);
5420
5421 LIST_ADDQ(&tcpchecks_list, &rs->list);
5422
5423 ruleset_found:
5424 rules->list = &rs->rules;
5425 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_SMTP_CHK);
5426
5427 out:
5428 free(errmsg);
5429 return err_code;
5430
5431 error:
5432 free(cmd);
5433 free(var);
5434 free_tcpcheck_vars(&rules->preset_vars);
5435 tcpcheck_ruleset_release(rs);
5436 err_code |= ERR_ALERT | ERR_FATAL;
5437 goto out;
5438}
Christopher Faulet811f78c2020-04-01 11:10:27 +02005439
Christopher Fauletce355072020-04-02 11:44:39 +02005440/* Parses the "option pgsql-check" proxy keyword */
5441int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5442 const char *file, int line)
5443{
5444 static char pgsql_req[] = {
5445 "%[var(check.plen),htonl,hex]" /* The packet length*/
5446 "00030000" /* the version 3.0 */
5447 "7573657200" /* "user" key */
5448 "%[var(check.username),hex]00" /* the username */
5449 "00"
5450 };
5451
5452 struct tcpcheck_ruleset *rs = NULL;
5453 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5454 struct tcpcheck_rule *chk;
5455 struct tcpcheck_var *var = NULL;
5456 char *user = NULL, *errmsg = NULL;
5457 size_t packetlen = 0;
5458 int err_code = 0;
5459
5460 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5461 err_code |= ERR_WARN;
5462
5463 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
5464 goto out;
5465
5466 if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
5467 ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
5468 file, line);
5469 err_code |= ERR_ALERT | ERR_FATAL;
5470 goto out;
5471 }
5472
5473
5474 curpx->options2 &= ~PR_O2_CHK_ANY;
5475 curpx->options2 |= PR_O2_TCPCHK_CHK;
5476
5477 free_tcpcheck_vars(&rules->preset_vars);
5478 rules->list = NULL;
5479 rules->flags = 0;
5480
5481 cur_arg += 2;
5482 if (!*args[cur_arg] || !*args[cur_arg+1]) {
5483 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
5484 file, line, args[0], args[1]);
5485 goto error;
5486 }
5487 if (strcmp(args[cur_arg], "user") == 0) {
5488 packetlen = 15 + strlen(args[cur_arg+1]);
5489 user = strdup(args[cur_arg+1]);
5490
5491 var = tcpcheck_var_create("check.username");
5492 if (user == NULL || var == NULL) {
5493 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5494 goto error;
5495 }
5496 var->data.type = SMP_T_STR;
5497 var->data.u.str.area = user;
5498 var->data.u.str.data = strlen(user);
5499 LIST_INIT(&var->list);
5500 LIST_ADDQ(&rules->preset_vars, &var->list);
5501 user = NULL;
5502 var = NULL;
5503
5504 var = tcpcheck_var_create("check.plen");
5505 if (var == NULL) {
5506 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5507 goto error;
5508 }
5509 var->data.type = SMP_T_SINT;
5510 var->data.u.sint = packetlen;
5511 LIST_INIT(&var->list);
5512 LIST_ADDQ(&rules->preset_vars, &var->list);
5513 var = NULL;
5514 }
5515 else {
5516 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
5517 file, line, args[0], args[1]);
5518 goto error;
5519 }
5520
5521 rs = tcpcheck_ruleset_lookup("*pgsql-check");
5522 if (rs)
5523 goto ruleset_found;
5524
5525 rs = tcpcheck_ruleset_create("*pgsql-check");
5526 if (rs == NULL) {
5527 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5528 goto error;
5529 }
5530
5531 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
5532 1, curpx, &rs->rules, file, line, &errmsg);
5533 if (!chk) {
5534 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5535 goto error;
5536 }
5537 chk->index = 0;
5538 LIST_ADDQ(&rs->rules, &chk->list);
5539
5540 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", pgsql_req, "log-format", ""},
5541 1, curpx, &rs->rules, file, line, &errmsg);
5542 if (!chk) {
5543 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5544 goto error;
5545 }
5546 chk->index = 1;
5547 LIST_ADDQ(&rs->rules, &chk->list);
5548
5549 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
5550 "min-recv", "5",
5551 "error-status", "L7RSP",
5552 "on-error", "%[check.payload(6,0)]",
5553 ""},
5554 1, curpx, &rs->rules, file, line, &errmsg);
5555 if (!chk) {
5556 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5557 goto error;
5558 }
5559 chk->index = 2;
5560 LIST_ADDQ(&rs->rules, &chk->list);
5561
5562 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^520000000800000000",
5563 "min-recv", "9",
5564 "error-status", "L7STS",
5565 "on-success", "PostgreSQL server is ok",
5566 "on-error", "PostgreSQL unknown error",
5567 ""},
5568 1, curpx, &rs->rules, file, line, &errmsg);
5569 if (!chk) {
5570 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5571 goto error;
5572 }
5573 chk->index = 3;
5574 LIST_ADDQ(&rs->rules, &chk->list);
5575
5576 LIST_ADDQ(&tcpchecks_list, &rs->list);
5577
5578 ruleset_found:
5579 rules->list = &rs->rules;
5580 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_PGSQL_CHK);
5581
5582 out:
5583 free(errmsg);
5584 return err_code;
5585
5586 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02005587 free(user);
5588 free(var);
5589 free_tcpcheck_vars(&rules->preset_vars);
5590 tcpcheck_ruleset_release(rs);
5591 err_code |= ERR_ALERT | ERR_FATAL;
5592 goto out;
5593}
5594
5595
5596/* Parses the "option mysql-check" proxy keyword */
5597int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5598 const char *file, int line)
5599{
5600 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
5601 * const char mysql40_client_auth_pkt[] = {
5602 * "\x0e\x00\x00" // packet length
5603 * "\x01" // packet number
5604 * "\x00\x00" // client capabilities
5605 * "\x00\x00\x01" // max packet
5606 * "haproxy\x00" // username (null terminated string)
5607 * "\x00" // filler (always 0x00)
5608 * "\x01\x00\x00" // packet length
5609 * "\x00" // packet number
5610 * "\x01" // COM_QUIT command
5611 * };
5612 */
5613 static char mysql40_rsname[] = "*mysql40-check";
5614 static char mysql40_req[] = {
5615 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
5616 "0080" /* client capabilities */
5617 "000001" /* max packet */
5618 "%[var(check.username),hex]00" /* the username */
5619 "00" /* filler (always 0x00) */
5620 "010000" /* packet length*/
5621 "00" /* sequence ID */
5622 "01" /* COM_QUIT command */
5623 };
5624
5625 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
5626 * const char mysql41_client_auth_pkt[] = {
5627 * "\x0e\x00\x00\" // packet length
5628 * "\x01" // packet number
5629 * "\x00\x00\x00\x00" // client capabilities
5630 * "\x00\x00\x00\x01" // max packet
5631 * "\x21" // character set (UTF-8)
5632 * char[23] // All zeroes
5633 * "haproxy\x00" // username (null terminated string)
5634 * "\x00" // filler (always 0x00)
5635 * "\x01\x00\x00" // packet length
5636 * "\x00" // packet number
5637 * "\x01" // COM_QUIT command
5638 * };
5639 */
5640 static char mysql41_rsname[] = "*mysql41-check";
5641 static char mysql41_req[] = {
5642 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
5643 "00820000" /* client capabilities */
5644 "00800001" /* max packet */
5645 "21" /* character set (UTF-8) */
5646 "000000000000000000000000" /* 23 bytes, al zeroes */
5647 "0000000000000000000000"
5648 "%[var(check.username),hex]00" /* the username */
5649 "00" /* filler (always 0x00) */
5650 "010000" /* packet length*/
5651 "00" /* sequence ID */
5652 "01" /* COM_QUIT command */
5653 };
5654
5655 struct tcpcheck_ruleset *rs = NULL;
5656 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5657 struct tcpcheck_rule *chk;
5658 struct tcpcheck_var *var = NULL;
5659 char *mysql_rsname = "*mysql-check";
5660 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
5661 int index = 0, err_code = 0;
5662
5663 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5664 err_code |= ERR_WARN;
5665
5666 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
5667 goto out;
5668
5669 if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
5670 ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
5671 file, line);
5672 err_code |= ERR_ALERT | ERR_FATAL;
5673 goto out;
5674 }
5675
5676 curpx->options2 &= ~PR_O2_CHK_ANY;
5677 curpx->options2 |= PR_O2_TCPCHK_CHK;
5678
5679 free_tcpcheck_vars(&rules->preset_vars);
5680 rules->list = NULL;
5681 rules->flags = 0;
5682
5683 cur_arg += 2;
5684 if (*args[cur_arg]) {
5685 char *user;
5686 int packetlen, userlen;
5687
5688 if (strcmp(args[cur_arg], "user") != 0) {
5689 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
5690 file, line, args[0], args[1], args[cur_arg]);
5691 goto error;
5692 }
5693
5694 if (*(args[cur_arg+1]) == 0) {
5695 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
5696 file, line, args[0], args[1], args[cur_arg]);
5697 goto error;
5698 }
5699
5700 hdr = calloc(4, sizeof(*hdr));
5701 user = strdup(args[cur_arg+1]);
5702 userlen = strlen(args[cur_arg+1]);
5703
5704 if (hdr == NULL || user == NULL) {
5705 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5706 goto error;
5707 }
5708
5709 if (*args[cur_arg+2]) {
5710 if (strcmp(args[cur_arg+2], "post-41") != 0) {
5711 ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41' (got '%s').\n",
5712 file, line, args[cur_arg], args[cur_arg+2]);
5713 goto error;
5714 }
5715 packetlen = userlen + 7 + 27;
5716 mysql_req = mysql41_req;
5717 mysql_rsname = mysql41_rsname;
5718 }
5719 else {
5720 packetlen = userlen + 7;
5721 mysql_req = mysql40_req;
5722 mysql_rsname = mysql40_rsname;
5723 }
5724
5725 hdr[0] = (unsigned char)(packetlen & 0xff);
5726 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
5727 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
5728 hdr[3] = 1;
5729
5730 var = tcpcheck_var_create("check.header");
5731 if (var == NULL) {
5732 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5733 goto error;
5734 }
5735 var->data.type = SMP_T_STR;
5736 var->data.u.str.area = hdr;
5737 var->data.u.str.data = 4;
5738 LIST_INIT(&var->list);
5739 LIST_ADDQ(&rules->preset_vars, &var->list);
5740 hdr = NULL;
5741 var = NULL;
5742
5743 var = tcpcheck_var_create("check.username");
5744 if (var == NULL) {
5745 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5746 goto error;
5747 }
5748 var->data.type = SMP_T_STR;
5749 var->data.u.str.area = user;
5750 var->data.u.str.data = strlen(user);
5751 LIST_INIT(&var->list);
5752 LIST_ADDQ(&rules->preset_vars, &var->list);
5753 user = NULL;
5754 var = NULL;
5755 }
5756
5757 rs = tcpcheck_ruleset_lookup(mysql_rsname);
5758 if (rs)
5759 goto ruleset_found;
5760
5761 rs = tcpcheck_ruleset_create(mysql_rsname);
5762 if (rs == NULL) {
5763 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5764 goto error;
5765 }
5766
5767 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
5768 1, curpx, &rs->rules, file, line, &errmsg);
5769 if (!chk) {
5770 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5771 goto error;
5772 }
5773 chk->index = index++;
5774 LIST_ADDQ(&rs->rules, &chk->list);
5775
5776 if (mysql_req) {
5777 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", mysql_req, "log-format", ""},
5778 1, curpx, &rs->rules, file, line, &errmsg);
5779 if (!chk) {
5780 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5781 goto error;
5782 }
5783 chk->index = index++;
5784 LIST_ADDQ(&rs->rules, &chk->list);
5785 }
5786
5787 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
5788 1, curpx, &rs->rules, file, line, &errmsg);
5789 if (!chk) {
5790 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5791 goto error;
5792 }
5793 chk->expect.custom = tcpcheck_mysql_expect_iniths;
5794 chk->index = index++;
5795 LIST_ADDQ(&rs->rules, &chk->list);
5796
5797 if (mysql_req) {
5798 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
5799 1, curpx, &rs->rules, file, line, &errmsg);
5800 if (!chk) {
5801 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5802 goto error;
5803 }
5804 chk->expect.custom = tcpcheck_mysql_expect_ok;
5805 chk->index = index++;
5806 LIST_ADDQ(&rs->rules, &chk->list);
5807 }
5808
5809 LIST_ADDQ(&tcpchecks_list, &rs->list);
5810
5811 ruleset_found:
5812 rules->list = &rs->rules;
5813 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_MYSQL_CHK);
5814
5815 out:
5816 free(errmsg);
5817 return err_code;
5818
5819 error:
5820 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02005821 free(user);
5822 free(var);
5823 free_tcpcheck_vars(&rules->preset_vars);
5824 tcpcheck_ruleset_release(rs);
5825 err_code |= ERR_ALERT | ERR_FATAL;
5826 goto out;
5827}
5828
Christopher Faulet1997eca2020-04-03 23:13:50 +02005829int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5830 const char *file, int line)
5831{
5832 static char *ldap_req = "300C020101600702010304008000";
5833
5834 struct tcpcheck_ruleset *rs = NULL;
5835 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5836 struct tcpcheck_rule *chk;
5837 char *errmsg = NULL;
5838 int err_code = 0;
5839
5840 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5841 err_code |= ERR_WARN;
5842
5843 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
5844 goto out;
5845
5846 if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
5847 ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
5848 file, line);
5849 err_code |= ERR_ALERT | ERR_FATAL;
5850 goto out;
5851 }
5852
5853 curpx->options2 &= ~PR_O2_CHK_ANY;
5854 curpx->options2 |= PR_O2_TCPCHK_CHK;
5855
5856 free_tcpcheck_vars(&rules->preset_vars);
5857 rules->list = NULL;
5858 rules->flags = 0;
5859
5860 rs = tcpcheck_ruleset_lookup("*ldap-check");
5861 if (rs)
5862 goto ruleset_found;
5863
5864 rs = tcpcheck_ruleset_create("*ldap-check");
5865 if (rs == NULL) {
5866 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5867 goto error;
5868 }
5869
5870 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
5871 1, curpx, &rs->rules, file, line, &errmsg);
5872 if (!chk) {
5873 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5874 goto error;
5875 }
5876 chk->index = 0;
5877 LIST_ADDQ(&rs->rules, &chk->list);
5878
5879 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
5880 "min-recv", "14",
5881 "on-error", "Not LDAPv3 protocol",
5882 ""},
5883 1, curpx, &rs->rules, file, line, &errmsg);
5884 if (!chk) {
5885 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5886 goto error;
5887 }
5888 chk->index = 1;
5889 LIST_ADDQ(&rs->rules, &chk->list);
5890
5891 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
5892 1, curpx, &rs->rules, file, line, &errmsg);
5893 if (!chk) {
5894 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5895 goto error;
5896 }
5897 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
5898 chk->index = 2;
5899 LIST_ADDQ(&rs->rules, &chk->list);
5900
5901 LIST_ADDQ(&tcpchecks_list, &rs->list);
5902
5903 ruleset_found:
5904 rules->list = &rs->rules;
5905 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_LDAP_CHK);
5906
5907 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02005908 free(errmsg);
5909 return err_code;
5910
5911 error:
5912 tcpcheck_ruleset_release(rs);
5913 err_code |= ERR_ALERT | ERR_FATAL;
5914 goto out;
5915}
5916
5917int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
5918 const char *file, int line)
5919{
5920 struct tcpcheck_ruleset *rs = NULL;
5921 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
5922 struct tcpcheck_rule *chk;
5923 char *spop_req = NULL;
5924 char *errmsg = NULL;
5925 int spop_len = 0, err_code = 0;
5926
5927 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
5928 err_code |= ERR_WARN;
5929
5930 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
5931 goto out;
5932
5933 if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
5934 ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
5935 file, line);
5936 err_code |= ERR_ALERT | ERR_FATAL;
5937 goto out;
5938 }
5939
5940 curpx->options2 &= ~PR_O2_CHK_ANY;
5941 curpx->options2 |= PR_O2_TCPCHK_CHK;
5942
5943 free_tcpcheck_vars(&rules->preset_vars);
5944 rules->list = NULL;
5945 rules->flags = 0;
5946
5947
5948 rs = tcpcheck_ruleset_lookup("*spop-check");
5949 if (rs)
5950 goto ruleset_found;
5951
5952 rs = tcpcheck_ruleset_create("*spop-check");
5953 if (rs == NULL) {
5954 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5955 goto error;
5956 }
5957
5958 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
5959 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
5960 goto error;
5961 }
5962 chunk_reset(&trash);
5963 dump_binary(&trash, spop_req, spop_len);
5964 trash.area[trash.data] = '\0';
5965
5966 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
5967 1, curpx, &rs->rules, file, line, &errmsg);
5968 if (!chk) {
5969 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5970 goto error;
5971 }
5972 chk->index = 0;
5973 LIST_ADDQ(&rs->rules, &chk->list);
5974
5975 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
5976 1, curpx, &rs->rules, file, line, &errmsg);
5977 if (!chk) {
5978 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
5979 goto error;
5980 }
5981 chk->expect.custom = tcpcheck_spop_expect_agenthello;
5982 chk->index = 1;
5983 LIST_ADDQ(&rs->rules, &chk->list);
5984
5985 LIST_ADDQ(&tcpchecks_list, &rs->list);
5986
5987 ruleset_found:
5988 rules->list = &rs->rules;
5989 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_SPOP_CHK);
5990
5991 out:
5992 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02005993 free(errmsg);
5994 return err_code;
5995
5996 error:
5997 tcpcheck_ruleset_release(rs);
5998 err_code |= ERR_ALERT | ERR_FATAL;
5999 goto out;
6000}
Christopher Fauletce355072020-04-02 11:44:39 +02006001
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006002
Christopher Fauletce8111e2020-04-06 15:04:11 +02006003/* Parse the "addr" server keyword */
6004static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6005 char **errmsg)
6006{
6007 struct sockaddr_storage *sk;
6008 struct protocol *proto;
6009 int port1, port2, err_code = 0;
6010
6011
6012 if (!*args[*cur_arg+1]) {
6013 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
6014 goto error;
6015 }
6016
6017 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
6018 if (!sk) {
6019 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
6020 goto error;
6021 }
6022
6023 proto = protocol_by_family(sk->ss_family);
6024 if (!proto || !proto->connect) {
6025 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
6026 args[*cur_arg], args[*cur_arg+1]);
6027 goto error;
6028 }
6029
6030 if (port1 != port2) {
6031 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
6032 args[*cur_arg], args[*cur_arg+1]);
6033 goto error;
6034 }
6035
6036 srv->check.addr = srv->agent.addr = *sk;
6037 srv->flags |= SRV_F_CHECKADDR;
6038 srv->flags |= SRV_F_AGENTADDR;
6039
6040 out:
6041 return err_code;
6042
6043 error:
6044 err_code |= ERR_ALERT | ERR_FATAL;
6045 goto out;
6046}
6047
6048
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006049/* Parse the "agent-addr" server keyword */
6050static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6051 char **errmsg)
6052{
6053 int err_code = 0;
6054
6055 if (!*(args[*cur_arg+1])) {
6056 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
6057 goto error;
6058 }
6059 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
6060 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
6061 goto error;
6062 }
6063
6064 out:
6065 return err_code;
6066
6067 error:
6068 err_code |= ERR_ALERT | ERR_FATAL;
6069 goto out;
6070}
6071
6072/* Parse the "agent-check" server keyword */
6073static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6074 char **errmsg)
6075{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006076 struct tcpcheck_ruleset *rs = NULL;
6077 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
6078 struct tcpcheck_rule *chk;
6079 int err_code = 0;
6080
6081 if (srv->do_agent)
6082 goto out;
6083
6084 if (!rules) {
6085 rules = calloc(1, sizeof(*rules));
6086 if (!rules) {
6087 memprintf(errmsg, "out of memory.");
6088 goto error;
6089 }
6090 LIST_INIT(&rules->preset_vars);
6091 srv->agent.tcpcheck_rules = rules;
6092 }
6093 rules->list = NULL;
6094 rules->flags = 0;
6095
6096 rs = tcpcheck_ruleset_lookup("*agent-check");
6097 if (rs)
6098 goto ruleset_found;
6099
6100 rs = tcpcheck_ruleset_create("*agent-check");
6101 if (rs == NULL) {
6102 memprintf(errmsg, "out of memory.");
6103 goto error;
6104 }
6105
6106 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", "%[var(check.agent_string)]", "log-format", ""},
6107 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
6108 if (!chk) {
6109 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
6110 goto error;
6111 }
6112 chk->index = 0;
6113 LIST_ADDQ(&rs->rules, &chk->list);
6114
6115 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
6116 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
6117 if (!chk) {
6118 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
6119 goto error;
6120 }
6121 chk->expect.custom = tcpcheck_agent_expect_reply;
6122 chk->index = 1;
6123 LIST_ADDQ(&rs->rules, &chk->list);
6124
6125 LIST_ADDQ(&tcpchecks_list, &rs->list);
6126
6127 ruleset_found:
6128 rules->list = &rs->rules;
6129 rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_AGENT_CHK);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006130 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006131
6132 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006133 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006134
6135 error:
6136 deinit_srv_agent_check(srv);
6137 tcpcheck_ruleset_release(rs);
6138 err_code |= ERR_ALERT | ERR_FATAL;
6139 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006140}
6141
6142/* Parse the "agent-inter" server keyword */
6143static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6144 char **errmsg)
6145{
6146 const char *err = NULL;
6147 unsigned int delay;
6148 int err_code = 0;
6149
6150 if (!*(args[*cur_arg+1])) {
6151 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
6152 goto error;
6153 }
6154
6155 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
6156 if (err == PARSE_TIME_OVER) {
6157 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
6158 args[*cur_arg+1], args[*cur_arg], srv->id);
6159 goto error;
6160 }
6161 else if (err == PARSE_TIME_UNDER) {
6162 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
6163 args[*cur_arg+1], args[*cur_arg], srv->id);
6164 goto error;
6165 }
6166 else if (err) {
6167 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
6168 *err, srv->id);
6169 goto error;
6170 }
6171 if (delay <= 0) {
6172 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
6173 delay, args[*cur_arg], srv->id);
6174 goto error;
6175 }
6176 srv->agent.inter = delay;
6177
6178 out:
6179 return err_code;
6180
6181 error:
6182 err_code |= ERR_ALERT | ERR_FATAL;
6183 goto out;
6184}
6185
6186/* Parse the "agent-port" server keyword */
6187static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6188 char **errmsg)
6189{
6190 int err_code = 0;
6191
6192 if (!*(args[*cur_arg+1])) {
6193 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
6194 goto error;
6195 }
6196
6197 global.maxsock++;
6198 srv->agent.port = atol(args[*cur_arg+1]);
6199
6200 out:
6201 return err_code;
6202
6203 error:
6204 err_code |= ERR_ALERT | ERR_FATAL;
6205 goto out;
6206}
6207
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006208int set_srv_agent_send(struct server *srv, const char *send)
6209{
6210 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
6211 struct tcpcheck_var *var = NULL;
6212 char *str;
6213
6214 str = strdup(send);
6215 var = tcpcheck_var_create("check.agent_string");
6216 if (str == NULL || var == NULL)
6217 goto error;
6218
6219 free_tcpcheck_vars(&rules->preset_vars);
6220
6221 var->data.type = SMP_T_STR;
6222 var->data.u.str.area = str;
6223 var->data.u.str.data = strlen(str);
6224 LIST_INIT(&var->list);
6225 LIST_ADDQ(&rules->preset_vars, &var->list);
6226
6227 return 1;
6228
6229 error:
6230 free(str);
6231 free(var);
6232 return 0;
6233}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006234
6235/* Parse the "agent-send" server keyword */
6236static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6237 char **errmsg)
6238{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006239 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006240 int err_code = 0;
6241
6242 if (!*(args[*cur_arg+1])) {
6243 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
6244 goto error;
6245 }
6246
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006247 if (!rules) {
6248 rules = calloc(1, sizeof(*rules));
6249 if (!rules) {
6250 memprintf(errmsg, "out of memory.");
6251 goto error;
6252 }
6253 LIST_INIT(&rules->preset_vars);
6254 srv->agent.tcpcheck_rules = rules;
6255 }
6256
6257 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006258 memprintf(errmsg, "out of memory.");
6259 goto error;
6260 }
6261
6262 out:
6263 return err_code;
6264
6265 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006266 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006267 err_code |= ERR_ALERT | ERR_FATAL;
6268 goto out;
6269}
6270
6271/* Parse the "no-agent-send" server keyword */
6272static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6273 char **errmsg)
6274{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02006275 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006276 return 0;
6277}
6278
Christopher Fauletce8111e2020-04-06 15:04:11 +02006279/* Parse the "check" server keyword */
6280static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6281 char **errmsg)
6282{
6283 srv->do_check = 1;
6284 return 0;
6285}
6286
6287/* Parse the "check-send-proxy" server keyword */
6288static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6289 char **errmsg)
6290{
6291 srv->check.send_proxy = 1;
6292 return 0;
6293}
6294
6295/* Parse the "check-via-socks4" server keyword */
6296static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6297 char **errmsg)
6298{
6299 srv->check.via_socks4 = 1;
6300 return 0;
6301}
6302
6303/* Parse the "no-check" server keyword */
6304static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6305 char **errmsg)
6306{
6307 deinit_srv_check(srv);
6308 return 0;
6309}
6310
6311/* Parse the "no-check-send-proxy" server keyword */
6312static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6313 char **errmsg)
6314{
6315 srv->check.send_proxy = 0;
6316 return 0;
6317}
6318
6319/* Parse the "rise" server keyword */
6320static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6321 char **errmsg)
6322{
6323 int err_code = 0;
6324
6325 if (!*args[*cur_arg + 1]) {
6326 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
6327 goto error;
6328 }
6329
6330 srv->check.rise = atol(args[*cur_arg+1]);
6331 if (srv->check.rise <= 0) {
6332 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
6333 goto error;
6334 }
6335
6336 if (srv->check.health)
6337 srv->check.health = srv->check.rise;
6338
6339 out:
6340 return err_code;
6341
6342 error:
6343 deinit_srv_agent_check(srv);
6344 err_code |= ERR_ALERT | ERR_FATAL;
6345 goto out;
6346 return 0;
6347}
6348
6349/* Parse the "fall" server keyword */
6350static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6351 char **errmsg)
6352{
6353 int err_code = 0;
6354
6355 if (!*args[*cur_arg + 1]) {
6356 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
6357 goto error;
6358 }
6359
6360 srv->check.fall = atol(args[*cur_arg+1]);
6361 if (srv->check.fall <= 0) {
6362 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
6363 goto error;
6364 }
6365
6366 out:
6367 return err_code;
6368
6369 error:
6370 deinit_srv_agent_check(srv);
6371 err_code |= ERR_ALERT | ERR_FATAL;
6372 goto out;
6373 return 0;
6374}
6375
6376/* Parse the "inter" server keyword */
6377static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6378 char **errmsg)
6379{
6380 const char *err = NULL;
6381 unsigned int delay;
6382 int err_code = 0;
6383
6384 if (!*(args[*cur_arg+1])) {
6385 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
6386 goto error;
6387 }
6388
6389 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
6390 if (err == PARSE_TIME_OVER) {
6391 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
6392 args[*cur_arg+1], args[*cur_arg], srv->id);
6393 goto error;
6394 }
6395 else if (err == PARSE_TIME_UNDER) {
6396 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
6397 args[*cur_arg+1], args[*cur_arg], srv->id);
6398 goto error;
6399 }
6400 else if (err) {
6401 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
6402 *err, srv->id);
6403 goto error;
6404 }
6405 if (delay <= 0) {
6406 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
6407 delay, args[*cur_arg], srv->id);
6408 goto error;
6409 }
6410 srv->check.inter = delay;
6411
6412 out:
6413 return err_code;
6414
6415 error:
6416 err_code |= ERR_ALERT | ERR_FATAL;
6417 goto out;
6418}
6419
6420
6421/* Parse the "fastinter" server keyword */
6422static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6423 char **errmsg)
6424{
6425 const char *err = NULL;
6426 unsigned int delay;
6427 int err_code = 0;
6428
6429 if (!*(args[*cur_arg+1])) {
6430 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
6431 goto error;
6432 }
6433
6434 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
6435 if (err == PARSE_TIME_OVER) {
6436 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
6437 args[*cur_arg+1], args[*cur_arg], srv->id);
6438 goto error;
6439 }
6440 else if (err == PARSE_TIME_UNDER) {
6441 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
6442 args[*cur_arg+1], args[*cur_arg], srv->id);
6443 goto error;
6444 }
6445 else if (err) {
6446 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
6447 *err, srv->id);
6448 goto error;
6449 }
6450 if (delay <= 0) {
6451 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
6452 delay, args[*cur_arg], srv->id);
6453 goto error;
6454 }
6455 srv->check.fastinter = delay;
6456
6457 out:
6458 return err_code;
6459
6460 error:
6461 err_code |= ERR_ALERT | ERR_FATAL;
6462 goto out;
6463}
6464
6465
6466/* Parse the "downinter" server keyword */
6467static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6468 char **errmsg)
6469{
6470 const char *err = NULL;
6471 unsigned int delay;
6472 int err_code = 0;
6473
6474 if (!*(args[*cur_arg+1])) {
6475 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
6476 goto error;
6477 }
6478
6479 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
6480 if (err == PARSE_TIME_OVER) {
6481 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
6482 args[*cur_arg+1], args[*cur_arg], srv->id);
6483 goto error;
6484 }
6485 else if (err == PARSE_TIME_UNDER) {
6486 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
6487 args[*cur_arg+1], args[*cur_arg], srv->id);
6488 goto error;
6489 }
6490 else if (err) {
6491 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
6492 *err, srv->id);
6493 goto error;
6494 }
6495 if (delay <= 0) {
6496 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
6497 delay, args[*cur_arg], srv->id);
6498 goto error;
6499 }
6500 srv->check.downinter = delay;
6501
6502 out:
6503 return err_code;
6504
6505 error:
6506 err_code |= ERR_ALERT | ERR_FATAL;
6507 goto out;
6508}
6509
6510/* Parse the "port" server keyword */
6511static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
6512 char **errmsg)
6513{
6514 int err_code = 0;
6515
6516 if (!*(args[*cur_arg+1])) {
6517 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
6518 goto error;
6519 }
6520
6521 global.maxsock++;
6522 srv->check.port = atol(args[*cur_arg+1]);
6523 srv->flags |= SRV_F_CHECKPORT;
6524
6525 out:
6526 return err_code;
6527
6528 error:
6529 err_code |= ERR_ALERT | ERR_FATAL;
6530 goto out;
6531}
6532
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006533static struct cfg_kw_list cfg_kws = {ILH, {
6534 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
6535 { 0, NULL, NULL },
6536}};
6537
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006538static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02006539 { "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 +02006540 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
6541 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
6542 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
6543 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
6544 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02006545 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
6546 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
6547 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006548 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02006549 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
6550 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
6551 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
6552 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
6553 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
6554 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
6555 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
6556 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006557 { NULL, NULL, 0 },
6558}};
6559
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006560INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02006561INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006562
Willy Tarreaubd741542010-03-16 18:46:54 +01006563/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02006564 * Local variables:
6565 * c-indent-level: 8
6566 * c-basic-offset: 8
6567 * End:
6568 */