blob: 1adbf56e254dcbb7bb469760844792100fbc61c8 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Willy Tarreau9b39dc52014-07-08 00:54:10 +020018#include <signal.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090019#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020020#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020021#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020023#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020024#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010025#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040027#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090028#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020030#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031#include <arpa/inet.h>
32
Willy Tarreau122eba92020-06-04 10:15:32 +020033#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020034#include <haproxy/api.h>
Christopher Fauletfd6c2292020-03-25 18:20:15 +010035#include <common/cfgparse.h>
Willy Tarreau4aa573d2020-06-04 18:21:56 +020036#include <haproxy/check.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020037#include <haproxy/chunk.h>
Willy Tarreaueb92deb2020-06-04 10:53:16 +020038#include <haproxy/dns.h>
Willy Tarreau2741c8c2020-06-02 11:28:02 +020039#include <haproxy/istbuf.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020040#include <haproxy/list.h>
Willy Tarreaua55c4542020-06-04 22:59:39 +020041#include <haproxy/queue.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020042#include <haproxy/regex.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020043#include <haproxy/tools.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020044#include <haproxy/time.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020045#include <haproxy/thread.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020046#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020047#include <haproxy/http_htx.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020048#include <haproxy/h1.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020049#include <haproxy/htx.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020050#include <haproxy/log.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020051#include <haproxy/proxy.h>
Willy Tarreau3727a8a2020-06-04 17:37:26 +020052#include <haproxy/signal.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020053#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020054#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020055#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020056#include <haproxy/task.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020057#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020058
Willy Tarreauf268ee82020-06-04 17:05:57 +020059#include <haproxy/global.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020060
Willy Tarreauaa74c4e2020-06-04 10:19:23 +020061#include <haproxy/arg.h>
Willy Tarreau0f6ffd62020-06-03 19:33:00 +020062#include <haproxy/fd.h>
Willy Tarreaufc8f6a82020-06-03 19:20:59 +020063#include <haproxy/port_range.h>
Willy Tarreaufc774542020-06-04 17:31:04 +020064#include <haproxy/proto_tcp.h>
Willy Tarreau2dd7c352020-06-03 15:26:55 +020065#include <haproxy/protocol.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020066#include <proto/server.h>
Willy Tarreau832ce652020-06-04 08:36:05 +020067#include <haproxy/proto_udp.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020068#include <haproxy/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020069
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020070static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010071
Christopher Faulet61cc8522020-04-20 14:54:42 +020072static int wake_srv_chk(struct conn_stream *cs);
73struct data_cb check_conn_cb = {
74 .wake = wake_srv_chk,
75 .name = "CHCK",
76};
Christopher Fauletd7e63962020-04-17 20:15:59 +020077
Christopher Fauletd7cee712020-04-21 13:45:00 +020078/* Global tree to share all tcp-checks */
79struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020080
81
Willy Tarreau8ceae722018-11-26 11:58:30 +010082DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
83DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020084
Gaetan Rivet05d692d2020-02-14 17:42:54 +010085/* Dummy frontend used to create all checks sessions. */
86static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020087
Christopher Faulet61cc8522020-04-20 14:54:42 +020088/**************************************************************************/
89/************************ Handle check results ****************************/
90/**************************************************************************/
91struct check_status {
92 short result; /* one of SRV_CHK_* */
93 char *info; /* human readable short info */
94 char *desc; /* long description */
95};
96
97struct analyze_status {
98 char *desc; /* description */
99 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
100};
101
Simon Horman63a4a822012-03-19 07:24:41 +0900102static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100103 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
104 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200105 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200106
Willy Tarreau23964182014-05-20 20:56:30 +0200107 /* Below we have finished checks */
108 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100109 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100110
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100111 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200112
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
114 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
115 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100117 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
118 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
119 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200120
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100121 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
122 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200123
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200124 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200125
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100126 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
127 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
128 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900129
130 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
131 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200132 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200133};
134
Simon Horman63a4a822012-03-19 07:24:41 +0900135static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100136 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
137
138 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
139 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
140
141 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
142 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
143 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
144 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
145
146 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
147 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
148 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
149};
150
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100151/* checks if <err> is a real error for errno or one that can be ignored, and
152 * return 0 for these ones or <err> for real ones.
153 */
154static inline int unclean_errno(int err)
155{
156 if (err == EAGAIN || err == EINPROGRESS ||
157 err == EISCONN || err == EALREADY)
158 return 0;
159 return err;
160}
161
Christopher Faulet61cc8522020-04-20 14:54:42 +0200162/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200163const char *get_check_status_description(short check_status) {
164
165 const char *desc;
166
167 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200168 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200169 else
170 desc = NULL;
171
172 if (desc && *desc)
173 return desc;
174 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200175 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200176}
177
Christopher Faulet61cc8522020-04-20 14:54:42 +0200178/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200179const char *get_check_status_info(short check_status) {
180
181 const char *info;
182
183 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200184 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200185 else
186 info = NULL;
187
188 if (info && *info)
189 return info;
190 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200191 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200192}
193
Christopher Faulet61cc8522020-04-20 14:54:42 +0200194/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100195const char *get_analyze_status(short analyze_status) {
196
197 const char *desc;
198
199 if (analyze_status < HANA_STATUS_SIZE)
200 desc = analyze_statuses[analyze_status].desc;
201 else
202 desc = NULL;
203
204 if (desc && *desc)
205 return desc;
206 else
207 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
208}
209
Christopher Faulet61cc8522020-04-20 14:54:42 +0200210/* Sets check->status, update check->duration and fill check->result with an
211 * adequate CHK_RES_* value. The new check->health is computed based on the
212 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200213 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200214 * Shows information in logs about failed health check if server is UP or
215 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200216 */
Simon Horman4a741432013-02-23 15:35:38 +0900217static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100218{
Simon Horman4a741432013-02-23 15:35:38 +0900219 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200220 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200221 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900222
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200223 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100224 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900225 check->desc[0] = '\0';
226 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200227 return;
228 }
229
Simon Horman4a741432013-02-23 15:35:38 +0900230 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200231 return;
232
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200233 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900234 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
235 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200236 } else
Simon Horman4a741432013-02-23 15:35:38 +0900237 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200238
Simon Horman4a741432013-02-23 15:35:38 +0900239 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200240 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900241 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200242
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100243 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900244 check->duration = -1;
245 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200246 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900247 check->duration = tv_ms_elapsed(&check->start, &now);
248 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200249 }
250
Willy Tarreau23964182014-05-20 20:56:30 +0200251 /* no change is expected if no state change occurred */
252 if (check->result == CHK_RES_NEUTRAL)
253 return;
254
Olivier Houchard0923fa42019-01-11 18:43:04 +0100255 /* If the check was really just sending a mail, it won't have an
256 * associated server, so we're done now.
257 */
258 if (!s)
259 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200260 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200261
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200262 switch (check->result) {
263 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200264 /* Failure to connect to the agent as a secondary check should not
265 * cause the server to be marked down.
266 */
267 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900268 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200269 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100270 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200271 report = 1;
272 check->health--;
273 if (check->health < check->rise)
274 check->health = 0;
275 }
276 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200277
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200278 case CHK_RES_PASSED:
279 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
280 if ((check->health < check->rise + check->fall - 1) &&
281 (check->result == CHK_RES_PASSED || check->health > 0)) {
282 report = 1;
283 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200284
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200285 if (check->health >= check->rise)
286 check->health = check->rise + check->fall - 1; /* OK now */
287 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200288
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200289 /* clear consecutive_errors if observing is enabled */
290 if (s->onerror)
291 s->consecutive_errors = 0;
292 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100293
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200294 default:
295 break;
296 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200297
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200298 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
299 (status != prev_status || report)) {
300 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200301 "%s check for %sserver %s/%s %s%s",
302 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200303 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100304 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100305 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200306 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200307
Emeric Brun5a133512017-10-19 14:42:30 +0200308 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200309
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100310 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200311 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
312 (check->health >= check->rise) ? check->fall : check->rise,
313 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200314
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200315 ha_warning("%s.\n", trash.area);
316 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
317 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200318 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200319}
320
Willy Tarreau4eec5472014-05-20 22:32:27 +0200321/* Marks the check <check>'s server down if the current check is already failed
322 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200323 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200324static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200325{
Simon Horman4a741432013-02-23 15:35:38 +0900326 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900327
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200328 /* The agent secondary check should only cause a server to be marked
329 * as down if check->status is HCHK_STATUS_L7STS, which indicates
330 * that the agent returned "fail", "stopped" or "down".
331 * The implication here is that failure to connect to the agent
332 * as a secondary check should not cause the server to be marked
333 * down. */
334 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
335 return;
336
Willy Tarreau4eec5472014-05-20 22:32:27 +0200337 if (check->health > 0)
338 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100339
Willy Tarreau4eec5472014-05-20 22:32:27 +0200340 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200341 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200342}
343
Willy Tarreauaf549582014-05-16 17:37:50 +0200344/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200345 * it isn't in maintenance, it is not tracking a down server and other checks
346 * comply. The rule is simple : by default, a server is up, unless any of the
347 * following conditions is true :
348 * - health check failed (check->health < rise)
349 * - agent check failed (agent->health < rise)
350 * - the server tracks a down server (track && track->state == STOPPED)
351 * Note that if the server has a slowstart, it will switch to STARTING instead
352 * of RUNNING. Also, only the health checks support the nolb mode, so the
353 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200354 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200355static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200356{
Simon Horman4a741432013-02-23 15:35:38 +0900357 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100358
Emeric Brun52a91d32017-08-31 14:41:55 +0200359 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200360 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100361
Emeric Brun52a91d32017-08-31 14:41:55 +0200362 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200363 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100364
Willy Tarreau3e048382014-05-21 10:30:54 +0200365 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
366 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100367
Willy Tarreau3e048382014-05-21 10:30:54 +0200368 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
369 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200370
Emeric Brun52a91d32017-08-31 14:41:55 +0200371 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200372 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100373
Emeric Brun5a133512017-10-19 14:42:30 +0200374 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100375}
376
Willy Tarreaudb58b792014-05-21 13:57:23 +0200377/* Marks the check <check> as valid and tries to set its server into stopping mode
378 * if it was running or starting, and provided it isn't in maintenance and other
379 * checks comply. The conditions for the server to be marked in stopping mode are
380 * the same as for it to be turned up. Also, only the health checks support the
381 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200382 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200383static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200384{
Simon Horman4a741432013-02-23 15:35:38 +0900385 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100386
Emeric Brun52a91d32017-08-31 14:41:55 +0200387 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200388 return;
389
Willy Tarreaudb58b792014-05-21 13:57:23 +0200390 if (check->state & CHK_ST_AGENT)
391 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100392
Emeric Brun52a91d32017-08-31 14:41:55 +0200393 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200394 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100395
Willy Tarreaudb58b792014-05-21 13:57:23 +0200396 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
397 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100398
Willy Tarreaudb58b792014-05-21 13:57:23 +0200399 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
400 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100401
Willy Tarreaub26881a2017-12-23 11:16:49 +0100402 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100403}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200404
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100405/* note: use health_adjust() only, which first checks that the observe mode is
406 * enabled.
407 */
408void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100409{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100410 int failed;
411 int expire;
412
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100413 if (s->observe >= HANA_OBS_SIZE)
414 return;
415
Willy Tarreaubb956662013-01-24 00:37:39 +0100416 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100417 return;
418
419 switch (analyze_statuses[status].lr[s->observe - 1]) {
420 case 1:
421 failed = 1;
422 break;
423
424 case 2:
425 failed = 0;
426 break;
427
428 default:
429 return;
430 }
431
432 if (!failed) {
433 /* good: clear consecutive_errors */
434 s->consecutive_errors = 0;
435 return;
436 }
437
Olivier Houchard7059c552019-03-08 18:49:32 +0100438 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100439
440 if (s->consecutive_errors < s->consecutive_errors_limit)
441 return;
442
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100443 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
444 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100445
446 switch (s->onerror) {
447 case HANA_ONERR_FASTINTER:
448 /* force fastinter - nothing to do here as all modes force it */
449 break;
450
451 case HANA_ONERR_SUDDTH:
452 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900453 if (s->check.health > s->check.rise)
454 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100455
456 /* no break - fall through */
457
458 case HANA_ONERR_FAILCHK:
459 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200460 set_server_check_status(&s->check, HCHK_STATUS_HANA,
461 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200462 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100463 break;
464
465 case HANA_ONERR_MARKDWN:
466 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900467 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200468 set_server_check_status(&s->check, HCHK_STATUS_HANA,
469 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200470 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100471 break;
472
473 default:
474 /* write a warning? */
475 break;
476 }
477
478 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100479 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100480
Simon Horman66183002013-02-23 10:16:43 +0900481 if (s->check.fastinter) {
482 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300483 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200484 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300485 /* requeue check task with new expire */
486 task_queue(s->check.task);
487 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100488 }
Willy Tarreauef781042010-01-27 11:53:01 +0100489}
490
Christopher Faulet61cc8522020-04-20 14:54:42 +0200491/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100492 * closed, keep errno intact as it is supposed to contain the valid error code.
493 * If no error is reported, check the socket's error queue using getsockopt().
494 * Warning, this must be done only once when returning from poll, and never
495 * after an I/O error was attempted, otherwise the error queue might contain
496 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
497 * socket. Returns non-zero if an error was reported, zero if everything is
498 * clean (including a properly closed socket).
499 */
500static int retrieve_errno_from_socket(struct connection *conn)
501{
502 int skerr;
503 socklen_t lskerr = sizeof(skerr);
504
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100505 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100506 return 1;
507
Willy Tarreau3c728722014-01-23 13:50:42 +0100508 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100509 return 0;
510
Willy Tarreau585744b2017-08-24 14:31:19 +0200511 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100512 errno = skerr;
513
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100514 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100515
516 if (!errno) {
517 /* we could not retrieve an error, that does not mean there is
518 * none. Just don't change anything and only report the prior
519 * error if any.
520 */
521 if (conn->flags & CO_FL_ERROR)
522 return 1;
523 else
524 return 0;
525 }
526
527 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
528 return 1;
529}
530
Christopher Faulet61cc8522020-04-20 14:54:42 +0200531/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100532 * and adjust the server status accordingly. It may make use of <errno_bck>
533 * if non-null when the caller is absolutely certain of its validity (eg:
534 * checked just after a syscall). If the caller doesn't have a valid errno,
535 * it can pass zero, and retrieve_errno_from_socket() will be called to try
536 * to extract errno from the socket. If no error is reported, it will consider
537 * the <expired> flag. This is intended to be used when a connection error was
538 * reported in conn->flags or when a timeout was reported in <expired>. The
539 * function takes care of not updating a server status which was already set.
540 * All situations where at least one of <expired> or CO_FL_ERROR are set
541 * produce a status.
542 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200543static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100544{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200545 struct conn_stream *cs = check->cs;
546 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100547 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200548 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200549 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100550
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100551 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100552 return;
553
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100554 errno = unclean_errno(errno_bck);
555 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100556 retrieve_errno_from_socket(conn);
557
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200558 if (conn && !(conn->flags & CO_FL_ERROR) &&
559 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100560 return;
561
562 /* we'll try to build a meaningful error message depending on the
563 * context of the error possibly present in conn->err_code, and the
564 * socket error possibly collected above. This is useful to know the
565 * exact step of the L6 layer (eg: SSL handshake).
566 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200567 chk = get_trash_chunk();
568
Christopher Faulet799f3a42020-04-07 12:06:14 +0200569 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200570 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200571 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200572 if (!step)
573 chunk_printf(chk, " at initial connection step of tcp-check");
574 else {
575 chunk_printf(chk, " at step %d of tcp-check", step);
576 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200577 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
578 if (check->current_step->connect.port)
579 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200580 else
581 chunk_appendf(chk, " (connect)");
582 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200583 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
584 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100585
586 switch (expect->type) {
587 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200588 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100589 break;
590 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200591 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100592 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200593 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200594 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100595 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200596 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100597 chunk_appendf(chk, " (expect binary regex)");
598 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200599 case TCPCHK_EXPECT_STRING_LF:
600 chunk_appendf(chk, " (expect log-format string)");
601 break;
602 case TCPCHK_EXPECT_BINARY_LF:
603 chunk_appendf(chk, " (expect log-format binary)");
604 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200605 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200606 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200607 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200608 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 chunk_appendf(chk, " (expect HTTP status regex)");
610 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200611 case TCPCHK_EXPECT_HTTP_HEADER:
612 chunk_appendf(chk, " (expect HTTP header pattern)");
613 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200614 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200615 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200616 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200617 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200618 chunk_appendf(chk, " (expect HTTP body regex)");
619 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200620 case TCPCHK_EXPECT_HTTP_BODY_LF:
621 chunk_appendf(chk, " (expect log-format HTTP body)");
622 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200623 case TCPCHK_EXPECT_CUSTOM:
624 chunk_appendf(chk, " (expect custom function)");
625 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100626 case TCPCHK_EXPECT_UNDEF:
627 chunk_appendf(chk, " (undefined expect!)");
628 break;
629 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200630 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200631 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200632 chunk_appendf(chk, " (send)");
633 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200634
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200635 if (check->current_step && check->current_step->comment)
636 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200637 }
638 }
639
Willy Tarreau00149122017-10-04 18:05:01 +0200640 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100641 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200642 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
643 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100644 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200645 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
646 chk->area);
647 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100648 }
649 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100650 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200651 chunk_printf(&trash, "%s%s", strerror(errno),
652 chk->area);
653 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100654 }
655 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200656 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100657 }
658 }
659
Willy Tarreau00149122017-10-04 18:05:01 +0200660 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200661 /* NOTE: this is reported after <fall> tries */
662 chunk_printf(chk, "No port available for the TCP connection");
663 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
664 }
665
Willy Tarreau00149122017-10-04 18:05:01 +0200666 if (!conn) {
667 /* connection allocation error before the connection was established */
668 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
669 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100670 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100671 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200672 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100673 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
674 else if (expired)
675 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200676
677 /*
678 * might be due to a server IP change.
679 * Let's trigger a DNS resolution if none are currently running.
680 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100681 if (check->server)
682 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200683
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100684 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100685 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100686 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200687 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100688 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
689 else if (expired)
690 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
691 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200692 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100693 /* I/O error after connection was established and before we could diagnose */
694 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
695 }
696 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200697 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
698
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100699 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200700 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
701 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200702 tout = check->current_step->expect.tout_status;
703 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100704 }
705
706 return;
707}
708
Willy Tarreaubaaee002006-06-26 02:48:02 +0200709
Christopher Faulet61cc8522020-04-20 14:54:42 +0200710/**************************************************************************/
711/*************** Init/deinit tcp-check rules and ruleset ******************/
712/**************************************************************************/
713/* Releases memory allocated for a log-format string */
714static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200715{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200716 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100717
Christopher Faulet61cc8522020-04-20 14:54:42 +0200718 list_for_each_entry_safe(lf, lfb, fmt, list) {
719 LIST_DEL(&lf->list);
720 release_sample_expr(lf->expr);
721 free(lf->arg);
722 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100723 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200724}
725
Christopher Faulet61cc8522020-04-20 14:54:42 +0200726/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
727static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100728{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200729 if (!hdr)
730 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200731
Christopher Faulet61cc8522020-04-20 14:54:42 +0200732 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200733 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200734 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100735}
736
Christopher Faulet61cc8522020-04-20 14:54:42 +0200737/* Releases memory allocated for an HTTP header list used in a tcp-check send
738 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200739 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200740static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200741{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200742 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200743
Christopher Faulet61cc8522020-04-20 14:54:42 +0200744 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
745 LIST_DEL(&hdr->list);
746 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200747 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200748}
749
Christopher Faulet61cc8522020-04-20 14:54:42 +0200750/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
751 * tcp-check was allocated using a memory pool (it is used to instantiate email
752 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200753 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200754static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200755{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200756 if (!rule)
757 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200758
Christopher Faulet61cc8522020-04-20 14:54:42 +0200759 free(rule->comment);
760 switch (rule->action) {
761 case TCPCHK_ACT_SEND:
762 switch (rule->send.type) {
763 case TCPCHK_SEND_STRING:
764 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200765 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200766 break;
767 case TCPCHK_SEND_STRING_LF:
768 case TCPCHK_SEND_BINARY_LF:
769 free_tcpcheck_fmt(&rule->send.fmt);
770 break;
771 case TCPCHK_SEND_HTTP:
772 free(rule->send.http.meth.str.area);
773 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200774 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200775 else
776 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200777 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200778 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
779 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200780 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200781 else
782 free_tcpcheck_fmt(&rule->send.http.body_fmt);
783 break;
784 case TCPCHK_SEND_UNDEF:
785 break;
786 }
787 break;
788 case TCPCHK_ACT_EXPECT:
789 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
790 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
791 release_sample_expr(rule->expect.status_expr);
792 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200793 case TCPCHK_EXPECT_HTTP_STATUS:
794 free(rule->expect.codes.codes);
795 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200796 case TCPCHK_EXPECT_STRING:
797 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200798 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200799 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200800 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200801 case TCPCHK_EXPECT_STRING_REGEX:
802 case TCPCHK_EXPECT_BINARY_REGEX:
803 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
804 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200805 regex_free(rule->expect.regex);
806 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200807 case TCPCHK_EXPECT_STRING_LF:
808 case TCPCHK_EXPECT_BINARY_LF:
809 case TCPCHK_EXPECT_HTTP_BODY_LF:
810 free_tcpcheck_fmt(&rule->expect.fmt);
811 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200812 case TCPCHK_EXPECT_HTTP_HEADER:
813 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
814 regex_free(rule->expect.hdr.name_re);
815 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
816 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
817 else
818 istfree(&rule->expect.hdr.name);
819
820 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
821 regex_free(rule->expect.hdr.value_re);
822 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
823 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
824 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
825 istfree(&rule->expect.hdr.value);
826 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200827 case TCPCHK_EXPECT_CUSTOM:
828 case TCPCHK_EXPECT_UNDEF:
829 break;
830 }
831 break;
832 case TCPCHK_ACT_CONNECT:
833 free(rule->connect.sni);
834 free(rule->connect.alpn);
835 release_sample_expr(rule->connect.port_expr);
836 break;
837 case TCPCHK_ACT_COMMENT:
838 break;
839 case TCPCHK_ACT_ACTION_KW:
840 free(rule->action_kw.rule);
841 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200842 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200843
844 if (in_pool)
845 pool_free(pool_head_tcpcheck_rule, rule);
846 else
847 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200848}
849
Christopher Faulet61cc8522020-04-20 14:54:42 +0200850/* Creates a tcp-check variable used in preset variables before executing a
851 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100852 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200853static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100854{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200855 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100856
Christopher Faulet61cc8522020-04-20 14:54:42 +0200857 var = calloc(1, sizeof(*var));
858 if (var == NULL)
859 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100860
Christopher Fauletb61caf42020-04-21 10:57:42 +0200861 var->name = istdup(name);
862 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200863 free(var);
864 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100865 }
Simon Horman98637e52014-06-20 12:30:16 +0900866
Christopher Faulet61cc8522020-04-20 14:54:42 +0200867 LIST_INIT(&var->list);
868 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900869}
870
Christopher Faulet61cc8522020-04-20 14:54:42 +0200871/* Releases memory allocated for a preset tcp-check variable */
872static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900873{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200874 if (!var)
875 return;
876
Christopher Fauletb61caf42020-04-21 10:57:42 +0200877 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200878 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
879 free(var->data.u.str.area);
880 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
881 free(var->data.u.meth.str.area);
882 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900883}
884
Christopher Faulet61cc8522020-04-20 14:54:42 +0200885/* Releases a list of preset tcp-check variables */
886static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900887{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200888 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200889
Christopher Faulet61cc8522020-04-20 14:54:42 +0200890 list_for_each_entry_safe(var, back, vars, list) {
891 LIST_DEL(&var->list);
892 free_tcpcheck_var(var);
893 }
Simon Horman98637e52014-06-20 12:30:16 +0900894}
895
Christopher Faulet61cc8522020-04-20 14:54:42 +0200896/* Duplicate a list of preset tcp-check variables */
897int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900898{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200899 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900900
Christopher Faulet61cc8522020-04-20 14:54:42 +0200901 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200902 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903 if (!new)
904 goto error;
905 new->data.type = var->data.type;
906 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
907 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
908 goto error;
909 if (var->data.type == SMP_T_STR)
910 new->data.u.str.area[new->data.u.str.data] = 0;
911 }
912 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
913 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
914 goto error;
915 new->data.u.str.area[new->data.u.str.data] = 0;
916 new->data.u.meth.meth = var->data.u.meth.meth;
917 }
918 else
919 new->data.u = var->data.u;
920 LIST_ADDQ(dst, &new->list);
921 }
922 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900923
Christopher Faulet61cc8522020-04-20 14:54:42 +0200924 error:
925 free(new);
926 return 0;
927}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200928
Christopher Faulet61cc8522020-04-20 14:54:42 +0200929/* Looks for a shared tcp-check ruleset given its name. */
930static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
931{
932 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200933 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900934
Christopher Fauletd7cee712020-04-21 13:45:00 +0200935 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
936 if (node) {
937 rs = container_of(node, typeof(*rs), node);
938 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200939 }
940 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900941}
942
Christopher Faulet56192cc2020-05-29 08:10:50 +0200943/* Creates a new shared tcp-check ruleset and insert it in shared_tcpchecks
944 * tree.
945 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200946static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900947{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200948 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900949
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 rs = calloc(1, sizeof(*rs));
951 if (rs == NULL)
952 return NULL;
953
Christopher Fauletd7cee712020-04-21 13:45:00 +0200954 rs->node.key = strdup(name);
955 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200956 free(rs);
957 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900958 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200959
Christopher Faulet61cc8522020-04-20 14:54:42 +0200960 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200961 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900963}
964
Christopher Faulet61cc8522020-04-20 14:54:42 +0200965/* Releases memory allocated by a tcp-check ruleset. */
966static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900967{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200968 struct tcpcheck_rule *r, *rb;
969 if (!rs)
970 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200971
Christopher Fauletd7cee712020-04-21 13:45:00 +0200972 ebpt_delete(&rs->node);
973 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200974 list_for_each_entry_safe(r, rb, &rs->rules, list) {
975 LIST_DEL(&r->list);
976 free_tcpcheck(r, 0);
977 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200978 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900979}
980
Christopher Faulet61cc8522020-04-20 14:54:42 +0200981
982/**************************************************************************/
983/**************** Everything about tcp-checks execution *******************/
984/**************************************************************************/
985/* Returns the id of a step in a tcp-check ruleset */
986static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200987{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200988 if (!rule)
989 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900990
Christopher Faulet61cc8522020-04-20 14:54:42 +0200991 /* no last started step => first step */
992 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900993 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900994
Christopher Faulet61cc8522020-04-20 14:54:42 +0200995 /* last step is the first implicit connect */
996 if (rule->index == 0 &&
997 rule->action == TCPCHK_ACT_CONNECT &&
998 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
999 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001000
Christopher Faulet61cc8522020-04-20 14:54:42 +02001001 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001002}
1003
Christopher Faulet61cc8522020-04-20 14:54:42 +02001004/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1005 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001006 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001007static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001008{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001010
Christopher Faulet61cc8522020-04-20 14:54:42 +02001011 list_for_each_entry(r, rules->list, list) {
1012 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1013 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001014 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001015 return NULL;
1016}
Cyril Bontéac92a062014-12-27 22:28:38 +01001017
Christopher Faulet61cc8522020-04-20 14:54:42 +02001018/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1019 * NULL if none was found.
1020 */
1021static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1022{
1023 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001024
Christopher Faulet61cc8522020-04-20 14:54:42 +02001025 list_for_each_entry_rev(r, rules->list, list) {
1026 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1027 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001028 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001029 return NULL;
1030}
Cyril Bontéac92a062014-12-27 22:28:38 +01001031
Christopher Faulet61cc8522020-04-20 14:54:42 +02001032/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1033 * <start> or NULL if non was found. If <start> is NULL, it relies on
1034 * get_first_tcpcheck_rule().
1035 */
1036static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1037{
1038 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001039
Christopher Faulet61cc8522020-04-20 14:54:42 +02001040 if (!start)
1041 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001042
Christopher Faulet61cc8522020-04-20 14:54:42 +02001043 r = LIST_NEXT(&start->list, typeof(r), list);
1044 list_for_each_entry_from(r, rules->list, list) {
1045 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1046 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001047 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001048 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001049}
Simon Horman98637e52014-06-20 12:30:16 +09001050
Simon Horman98637e52014-06-20 12:30:16 +09001051
Christopher Faulet61cc8522020-04-20 14:54:42 +02001052/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1053static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1054 int match, struct ist info)
1055{
1056 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001057
Christopher Faulet61cc8522020-04-20 14:54:42 +02001058 /* Follows these step to produce the info message:
1059 * 1. if info field is already provided, copy it
1060 * 2. if the expect rule provides an onerror log-format string,
1061 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001062 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001063 * 4. Otherwise produce the generic tcp-check info message
1064 */
1065 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001066 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001067 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001068 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1070 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1071 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001072 }
Simon Horman98637e52014-06-20 12:30:16 +09001073
Christopher Faulet61cc8522020-04-20 14:54:42 +02001074 if (check->type == PR_O2_TCPCHK_CHK &&
1075 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1076 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001077
Christopher Faulet61cc8522020-04-20 14:54:42 +02001078 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1079 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001080 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001081 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1082 break;
1083 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001084 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001085 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 tcpcheck_get_step_id(check, rule));
1087 break;
1088 case TCPCHK_EXPECT_BINARY:
1089 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1090 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001091 case TCPCHK_EXPECT_STRING_REGEX:
1092 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1093 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001094 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1095 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001096 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001097 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001098 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001099 case TCPCHK_EXPECT_STRING_LF:
1100 case TCPCHK_EXPECT_HTTP_BODY_LF:
1101 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1102 break;
1103 case TCPCHK_EXPECT_BINARY_LF:
1104 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1105 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001106 case TCPCHK_EXPECT_CUSTOM:
1107 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1108 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001109 case TCPCHK_EXPECT_HTTP_HEADER:
1110 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001111 case TCPCHK_EXPECT_UNDEF:
1112 /* Should never happen. */
1113 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001114 }
1115
Christopher Faulet61cc8522020-04-20 14:54:42 +02001116 comment:
1117 /* If the failing expect rule provides a comment, it is concatenated to
1118 * the info message.
1119 */
1120 if (rule->comment) {
1121 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001122 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001123 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001124
Christopher Faulet61cc8522020-04-20 14:54:42 +02001125 /* Finally, the check status code is set if the failing expect rule
1126 * defines a status expression.
1127 */
1128 if (rule->expect.status_expr) {
1129 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001130 rule->expect.status_expr, SMP_T_STR);
1131
1132 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1133 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001134 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001135 }
Simon Horman98637e52014-06-20 12:30:16 +09001136
Christopher Faulet61cc8522020-04-20 14:54:42 +02001137 *(b_tail(msg)) = '\0';
1138}
Cyril Bontéac92a062014-12-27 22:28:38 +01001139
Christopher Faulet61cc8522020-04-20 14:54:42 +02001140/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1141static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1142 struct ist info)
1143{
1144 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001145
Christopher Faulet61cc8522020-04-20 14:54:42 +02001146 /* Follows these step to produce the info message:
1147 * 1. if info field is already provided, copy it
1148 * 2. if the expect rule provides an onsucces log-format string,
1149 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001150 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001151 * 4. Otherwise produce the generic tcp-check info message
1152 */
1153 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001154 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001155 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1156 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1157 &rule->expect.onsuccess_fmt);
1158 else if (check->type == PR_O2_TCPCHK_CHK &&
1159 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1160 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001161
Christopher Faulet61cc8522020-04-20 14:54:42 +02001162 /* Finally, the check status code is set if the expect rule defines a
1163 * status expression.
1164 */
1165 if (rule->expect.status_expr) {
1166 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001167 rule->expect.status_expr, SMP_T_STR);
1168
1169 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1170 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001171 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001172 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001173
1174 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001175}
1176
Christopher Faulet61cc8522020-04-20 14:54:42 +02001177/* Builds the server state header used by HTTP health-checks */
1178static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001179{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001180 int sv_state;
1181 int ratio;
1182 char addr[46];
1183 char port[6];
1184 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1185 "UP %d/%d", "UP",
1186 "NOLB %d/%d", "NOLB",
1187 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001188
Christopher Faulet61cc8522020-04-20 14:54:42 +02001189 if (!(s->check.state & CHK_ST_ENABLED))
1190 sv_state = 6;
1191 else if (s->cur_state != SRV_ST_STOPPED) {
1192 if (s->check.health == s->check.rise + s->check.fall - 1)
1193 sv_state = 3; /* UP */
1194 else
1195 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001196
Christopher Faulet61cc8522020-04-20 14:54:42 +02001197 if (s->cur_state == SRV_ST_STOPPING)
1198 sv_state += 2;
1199 } else {
1200 if (s->check.health)
1201 sv_state = 1; /* going up */
1202 else
1203 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001204 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001205
Christopher Faulet61cc8522020-04-20 14:54:42 +02001206 chunk_appendf(buf, srv_hlt_st[sv_state],
1207 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1208 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001209
Christopher Faulet61cc8522020-04-20 14:54:42 +02001210 addr_to_str(&s->addr, addr, sizeof(addr));
1211 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1212 snprintf(port, sizeof(port), "%u", s->svc_port);
1213 else
1214 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001215
Christopher Faulet61cc8522020-04-20 14:54:42 +02001216 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1217 addr, port, s->proxy->id, s->id,
1218 global.node,
1219 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1220 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1221 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1222 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001223
Christopher Faulet61cc8522020-04-20 14:54:42 +02001224 if ((s->cur_state == SRV_ST_STARTING) &&
1225 now.tv_sec < s->last_change + s->slowstart &&
1226 now.tv_sec >= s->last_change) {
1227 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1228 chunk_appendf(buf, "; throttle=%d%%", ratio);
1229 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001230
Christopher Faulet61cc8522020-04-20 14:54:42 +02001231 return b_data(buf);
1232}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001233
Christopher Faulet61cc8522020-04-20 14:54:42 +02001234/* Internal functions to parse and validate a MySQL packet in the context of an
1235 * expect rule. It start to parse the input buffer at the offset <offset>. If
1236 * <last_read> is set, no more data are expected.
1237 */
1238static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1239 unsigned int offset, int last_read)
1240{
1241 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1242 enum healthcheck_status status;
1243 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001244 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001245 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001246
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001247
Christopher Faulet61cc8522020-04-20 14:54:42 +02001248 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001249 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 if (!last_read)
1251 goto wait_more_data;
1252
1253 /* invalid length or truncated response */
1254 status = HCHK_STATUS_L7RSP;
1255 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001256 }
1257
Christopher Faulet61cc8522020-04-20 14:54:42 +02001258 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1259 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1260 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001261
Christopher Faulet61cc8522020-04-20 14:54:42 +02001262 if (b_data(&check->bi) < offset+plen+4) {
1263 if (!last_read)
1264 goto wait_more_data;
1265
1266 /* invalid length or truncated response */
1267 status = HCHK_STATUS_L7RSP;
1268 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001269 }
Simon Horman98637e52014-06-20 12:30:16 +09001270
Christopher Faulet61cc8522020-04-20 14:54:42 +02001271 if (*b_peek(&check->bi, offset+4) == '\xff') {
1272 /* MySQL Error packet always begin with field_count = 0xff */
1273 status = HCHK_STATUS_L7STS;
1274 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1275 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1276 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1277 goto error;
1278 }
Simon Horman98637e52014-06-20 12:30:16 +09001279
Christopher Faulet61cc8522020-04-20 14:54:42 +02001280 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1281 /* Not the last rule, continue */
1282 goto out;
1283 }
Simon Horman98637e52014-06-20 12:30:16 +09001284
Christopher Faulet61cc8522020-04-20 14:54:42 +02001285 /* We set the MySQL Version in description for information purpose
1286 * FIXME : it can be cool to use MySQL Version for other purpose,
1287 * like mark as down old MySQL server.
1288 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001289 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1290 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001291
Christopher Faulet61cc8522020-04-20 14:54:42 +02001292 out:
1293 free_trash_chunk(msg);
1294 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001295
Christopher Faulet61cc8522020-04-20 14:54:42 +02001296 error:
1297 ret = TCPCHK_EVAL_STOP;
1298 check->code = err;
1299 msg = alloc_trash_chunk();
1300 if (msg)
1301 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1302 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1303 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001304
Christopher Faulet61cc8522020-04-20 14:54:42 +02001305 wait_more_data:
1306 ret = TCPCHK_EVAL_WAIT;
1307 goto out;
1308}
Simon Horman98637e52014-06-20 12:30:16 +09001309
Christopher Faulet61cc8522020-04-20 14:54:42 +02001310/* Custom tcp-check expect function to parse and validate the MySQL initial
1311 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1312 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1313 * error occurred.
1314 */
1315static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1316{
1317 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1318}
Simon Horman98637e52014-06-20 12:30:16 +09001319
Christopher Faulet61cc8522020-04-20 14:54:42 +02001320/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1321 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1322 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1323 * an error occurred.
1324 */
1325static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1326{
1327 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001328
Christopher Faulet61cc8522020-04-20 14:54:42 +02001329 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1330 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1331 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001332
Christopher Faulet61cc8522020-04-20 14:54:42 +02001333 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1334}
Simon Horman98637e52014-06-20 12:30:16 +09001335
Christopher Faulet61cc8522020-04-20 14:54:42 +02001336/* Custom tcp-check expect function to parse and validate the LDAP bind response
1337 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1338 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1339 * error occurred.
1340 */
1341static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1342{
1343 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1344 enum healthcheck_status status;
1345 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001346 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001347 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001348
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 /* Check if the server speaks LDAP (ASN.1/BER)
1350 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1351 * http://tools.ietf.org/html/rfc4511
1352 */
1353 /* size of LDAPMessage */
1354 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001355
Christopher Faulet61cc8522020-04-20 14:54:42 +02001356 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1357 * messageID: 0x02 0x01 0x01: INTEGER 1
1358 * protocolOp: 0x61: bindResponse
1359 */
1360 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1361 status = HCHK_STATUS_L7RSP;
1362 desc = ist("Not LDAPv3 protocol");
1363 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001364 }
Simon Horman98637e52014-06-20 12:30:16 +09001365
Christopher Faulet61cc8522020-04-20 14:54:42 +02001366 /* size of bindResponse */
1367 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001368
Christopher Faulet61cc8522020-04-20 14:54:42 +02001369 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1370 * ldapResult: 0x0a 0x01: ENUMERATION
1371 */
1372 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1373 status = HCHK_STATUS_L7RSP;
1374 desc = ist("Not LDAPv3 protocol");
1375 goto error;
1376 }
Simon Horman98637e52014-06-20 12:30:16 +09001377
Christopher Faulet61cc8522020-04-20 14:54:42 +02001378 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1379 * resultCode
1380 */
1381 check->code = *(b_head(&check->bi) + msglen + 9);
1382 if (check->code) {
1383 status = HCHK_STATUS_L7STS;
1384 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1385 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001386 }
1387
Christopher Faulet1941bab2020-05-05 07:55:50 +02001388 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1389 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001390
Christopher Faulet61cc8522020-04-20 14:54:42 +02001391 out:
1392 free_trash_chunk(msg);
1393 return ret;
1394
1395 error:
1396 ret = TCPCHK_EVAL_STOP;
1397 msg = alloc_trash_chunk();
1398 if (msg)
1399 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1400 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1401 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001402}
1403
Christopher Faulet61cc8522020-04-20 14:54:42 +02001404/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1405 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1406 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001407 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001408static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read)
Willy Tarreaubaaee002006-06-26 02:48:02 +02001409{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1411 enum healthcheck_status status;
1412 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001413 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001414 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001415
Willy Tarreaubaaee002006-06-26 02:48:02 +02001416
Christopher Faulet61cc8522020-04-20 14:54:42 +02001417 memcpy(&framesz, b_head(&check->bi), 4);
1418 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001419
Christopher Faulet61cc8522020-04-20 14:54:42 +02001420 if (!last_read && b_data(&check->bi) < (4+framesz))
1421 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001422
Christopher Faulet61cc8522020-04-20 14:54:42 +02001423 memset(b_orig(&trash), 0, b_size(&trash));
1424 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1425 status = HCHK_STATUS_L7RSP;
1426 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1427 goto error;
1428 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001429
Christopher Faulet1941bab2020-05-05 07:55:50 +02001430 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1431 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001432
Christopher Faulet61cc8522020-04-20 14:54:42 +02001433 out:
1434 free_trash_chunk(msg);
1435 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001436
Christopher Faulet61cc8522020-04-20 14:54:42 +02001437 error:
1438 ret = TCPCHK_EVAL_STOP;
1439 msg = alloc_trash_chunk();
1440 if (msg)
1441 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1442 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1443 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001444
Christopher Faulet61cc8522020-04-20 14:54:42 +02001445 wait_more_data:
1446 ret = TCPCHK_EVAL_WAIT;
1447 goto out;
1448}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001449
Christopher Faulet61cc8522020-04-20 14:54:42 +02001450/* Custom tcp-check expect function to parse and validate the agent-check
1451 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1452 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1453 */
1454static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1455{
1456 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1457 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1458 const char *hs = NULL; /* health status */
1459 const char *as = NULL; /* admin status */
1460 const char *ps = NULL; /* performance status */
1461 const char *cs = NULL; /* maxconn */
1462 const char *err = NULL; /* first error to report */
1463 const char *wrn = NULL; /* first warning to report */
1464 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001465
Christopher Faulet61cc8522020-04-20 14:54:42 +02001466 /* We're getting an agent check response. The agent could
1467 * have been disabled in the mean time with a long check
1468 * still pending. It is important that we ignore the whole
1469 * response.
1470 */
1471 if (!(check->state & CHK_ST_ENABLED))
1472 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001473
Christopher Faulet61cc8522020-04-20 14:54:42 +02001474 /* The agent supports strings made of a single line ended by the
1475 * first CR ('\r') or LF ('\n'). This line is composed of words
1476 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1477 * line may optionally contained a description of a state change
1478 * after a sharp ('#'), which is only considered if a health state
1479 * is announced.
1480 *
1481 * Words may be composed of :
1482 * - a numeric weight suffixed by the percent character ('%').
1483 * - a health status among "up", "down", "stopped", and "fail".
1484 * - an admin status among "ready", "drain", "maint".
1485 *
1486 * These words may appear in any order. If multiple words of the
1487 * same category appear, the last one wins.
1488 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001489
Christopher Faulet61cc8522020-04-20 14:54:42 +02001490 p = b_head(&check->bi);
1491 while (*p && *p != '\n' && *p != '\r')
1492 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001493
Christopher Faulet61cc8522020-04-20 14:54:42 +02001494 if (!*p) {
1495 if (!last_read)
1496 goto wait_more_data;
1497
1498 /* at least inform the admin that the agent is mis-behaving */
1499 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1500 goto out;
1501 }
1502
1503 *p = 0;
1504 cmd = b_head(&check->bi);
1505
1506 while (*cmd) {
1507 /* look for next word */
1508 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1509 cmd++;
1510 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001511 }
1512
Christopher Faulet61cc8522020-04-20 14:54:42 +02001513 if (*cmd == '#') {
1514 /* this is the beginning of a health status description,
1515 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001516 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001517 cmd++;
1518 while (*cmd == '\t' || *cmd == ' ')
1519 cmd++;
1520 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001521 }
1522
Christopher Faulet61cc8522020-04-20 14:54:42 +02001523 /* find the end of the word so that we have a null-terminated
1524 * word between <cmd> and <p>.
1525 */
1526 p = cmd + 1;
1527 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1528 p++;
1529 if (*p)
1530 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001531
Christopher Faulet61cc8522020-04-20 14:54:42 +02001532 /* first, health statuses */
1533 if (strcasecmp(cmd, "up") == 0) {
1534 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1535 status = HCHK_STATUS_L7OKD;
1536 hs = cmd;
1537 }
1538 else if (strcasecmp(cmd, "down") == 0) {
1539 check->server->check.health = 0;
1540 status = HCHK_STATUS_L7STS;
1541 hs = cmd;
1542 }
1543 else if (strcasecmp(cmd, "stopped") == 0) {
1544 check->server->check.health = 0;
1545 status = HCHK_STATUS_L7STS;
1546 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001547 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001548 else if (strcasecmp(cmd, "fail") == 0) {
1549 check->server->check.health = 0;
1550 status = HCHK_STATUS_L7STS;
1551 hs = cmd;
1552 }
1553 /* admin statuses */
1554 else if (strcasecmp(cmd, "ready") == 0) {
1555 as = cmd;
1556 }
1557 else if (strcasecmp(cmd, "drain") == 0) {
1558 as = cmd;
1559 }
1560 else if (strcasecmp(cmd, "maint") == 0) {
1561 as = cmd;
1562 }
1563 /* try to parse a weight here and keep the last one */
1564 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1565 ps = cmd;
1566 }
1567 /* try to parse a maxconn here */
1568 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1569 cs = cmd;
1570 }
1571 else {
1572 /* keep a copy of the first error */
1573 if (!err)
1574 err = cmd;
1575 }
1576 /* skip to next word */
1577 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001578 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001579 /* here, cmd points either to \0 or to the beginning of a
1580 * description. Skip possible leading spaces.
1581 */
1582 while (*cmd == ' ' || *cmd == '\n')
1583 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001584
Christopher Faulet61cc8522020-04-20 14:54:42 +02001585 /* First, update the admin status so that we avoid sending other
1586 * possibly useless warnings and can also update the health if
1587 * present after going back up.
1588 */
1589 if (as) {
1590 if (strcasecmp(as, "drain") == 0)
1591 srv_adm_set_drain(check->server);
1592 else if (strcasecmp(as, "maint") == 0)
1593 srv_adm_set_maint(check->server);
1594 else
1595 srv_adm_set_ready(check->server);
1596 }
Simon Horman98637e52014-06-20 12:30:16 +09001597
Christopher Faulet61cc8522020-04-20 14:54:42 +02001598 /* now change weights */
1599 if (ps) {
1600 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001601
Christopher Faulet61cc8522020-04-20 14:54:42 +02001602 msg = server_parse_weight_change_request(check->server, ps);
1603 if (!wrn || !*wrn)
1604 wrn = msg;
1605 }
Simon Horman98637e52014-06-20 12:30:16 +09001606
Christopher Faulet61cc8522020-04-20 14:54:42 +02001607 if (cs) {
1608 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001609
Christopher Faulet61cc8522020-04-20 14:54:42 +02001610 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001611
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 msg = server_parse_maxconn_change_request(check->server, cs);
1613 if (!wrn || !*wrn)
1614 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001615 }
1616
Christopher Faulet61cc8522020-04-20 14:54:42 +02001617 /* and finally health status */
1618 if (hs) {
1619 /* We'll report some of the warnings and errors we have
1620 * here. Down reports are critical, we leave them untouched.
1621 * Lack of report, or report of 'UP' leaves the room for
1622 * ERR first, then WARN.
1623 */
1624 const char *msg = cmd;
1625 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001626
Christopher Faulet61cc8522020-04-20 14:54:42 +02001627 if (!*msg || status == HCHK_STATUS_L7OKD) {
1628 if (err && *err)
1629 msg = err;
1630 else if (wrn && *wrn)
1631 msg = wrn;
1632 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001633
Christopher Faulet61cc8522020-04-20 14:54:42 +02001634 t = get_trash_chunk();
1635 chunk_printf(t, "via agent : %s%s%s%s",
1636 hs, *msg ? " (" : "",
1637 msg, *msg ? ")" : "");
1638 set_server_check_status(check, status, t->area);
1639 }
1640 else if (err && *err) {
1641 /* No status change but we'd like to report something odd.
1642 * Just report the current state and copy the message.
1643 */
1644 chunk_printf(&trash, "agent reports an error : %s", err);
1645 set_server_check_status(check, status/*check->status*/, trash.area);
1646 }
1647 else if (wrn && *wrn) {
1648 /* No status change but we'd like to report something odd.
1649 * Just report the current state and copy the message.
1650 */
1651 chunk_printf(&trash, "agent warns : %s", wrn);
1652 set_server_check_status(check, status/*check->status*/, trash.area);
1653 }
1654 else
1655 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001656
Christopher Faulet61cc8522020-04-20 14:54:42 +02001657 out:
1658 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001659
Christopher Faulet61cc8522020-04-20 14:54:42 +02001660 wait_more_data:
1661 ret = TCPCHK_EVAL_WAIT;
1662 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001663}
1664
Christopher Faulet61cc8522020-04-20 14:54:42 +02001665/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1666 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1667 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001668 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001669static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001670{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001671 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1672 struct tcpcheck_connect *connect = &rule->connect;
1673 struct proxy *proxy = check->proxy;
1674 struct server *s = check->server;
1675 struct task *t = check->task;
1676 struct conn_stream *cs;
1677 struct connection *conn = NULL;
1678 struct protocol *proto;
1679 struct xprt_ops *xprt;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001680 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001681 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001682
Christopher Faulet61cc8522020-04-20 14:54:42 +02001683 /* For a connect action we'll create a new connection. We may also have
1684 * to kill a previous one. But we don't want to leave *without* a
1685 * connection if we came here from the connection layer, hence with a
1686 * connection. Thus we'll proceed in the following order :
1687 * 1: close but not release previous connection (handled by the caller)
1688 * 2: try to get a new connection
1689 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001690 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001691
Christopher Faulet61cc8522020-04-20 14:54:42 +02001692 /* 2- prepare new connection */
1693 cs = cs_new(NULL);
1694 if (!cs) {
1695 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1696 tcpcheck_get_step_id(check, rule));
1697 if (rule->comment)
1698 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1699 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1700 ret = TCPCHK_EVAL_STOP;
1701 goto out;
1702 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001703
Christopher Faulet61cc8522020-04-20 14:54:42 +02001704 /* 3- release and replace the old one on success */
1705 if (check->cs) {
1706 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001707 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1708 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001709
1710 /* We may have been scheduled to run, and the I/O handler
1711 * expects to have a cs, so remove the tasklet
1712 */
1713 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1714 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001715 }
1716
Christopher Faulet61cc8522020-04-20 14:54:42 +02001717 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001718
Christopher Faulet61cc8522020-04-20 14:54:42 +02001719 check->cs = cs;
1720 conn = cs->conn;
1721 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001722
Christopher Faulet61cc8522020-04-20 14:54:42 +02001723 /* Maybe there were an older connection we were waiting on */
1724 check->wait_list.events = 0;
1725 conn->target = s ? &s->obj_type : &proxy->obj_type;
1726
1727 /* no client address */
1728 if (!sockaddr_alloc(&conn->dst)) {
1729 status = SF_ERR_RESOURCE;
1730 goto fail_check;
1731 }
1732
1733 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001734 * addr if specified on the server. otherwise, use the server addr (it
1735 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001736 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001737 *conn->dst = (is_addr(&connect->addr)
1738 ? connect->addr
1739 : (is_addr(&check->addr) ? check->addr : s->addr));
1740 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001741
Christopher Faulet61cc8522020-04-20 14:54:42 +02001742 port = 0;
1743 if (!port && connect->port)
1744 port = connect->port;
1745 if (!port && connect->port_expr) {
1746 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001747
Christopher Faulet61cc8522020-04-20 14:54:42 +02001748 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1749 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1750 connect->port_expr, SMP_T_SINT);
1751 if (smp)
1752 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001753 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001754 if (!port && is_inet_addr(&connect->addr))
1755 port = get_host_port(&connect->addr);
1756 if (!port && check->port)
1757 port = check->port;
1758 if (!port && is_inet_addr(&check->addr))
1759 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001760 if (!port) {
1761 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001762 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001763 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001764 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001765
Christopher Faulet61cc8522020-04-20 14:54:42 +02001766 xprt = ((connect->options & TCPCHK_OPT_SSL)
1767 ? xprt_get(XPRT_SSL)
1768 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001769
Christopher Faulet61cc8522020-04-20 14:54:42 +02001770 conn_prepare(conn, proto, xprt);
1771 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001772
Christopher Faulet61cc8522020-04-20 14:54:42 +02001773 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001774 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001775 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001776 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001777
Christopher Faulet61cc8522020-04-20 14:54:42 +02001778 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1779 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001780 if (!next || next->action != TCPCHK_ACT_EXPECT)
1781 flags |= CONNECT_DELACK_ALWAYS;
1782 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001783 }
1784
Christopher Faulet61cc8522020-04-20 14:54:42 +02001785 if (status != SF_ERR_NONE)
1786 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001787
Christopher Faulet61cc8522020-04-20 14:54:42 +02001788 conn->flags |= CO_FL_PRIVATE;
1789 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001790
Christopher Faulet61cc8522020-04-20 14:54:42 +02001791 /* The mux may be initialized now if there isn't server attached to the
1792 * check (email alerts) or if there is a mux proto specified or if there
1793 * is no alpn.
1794 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001795 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1796 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001797 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001798
Christopher Faulet61cc8522020-04-20 14:54:42 +02001799 if (connect->mux_proto)
1800 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001801 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001802 mux_ops = check->mux_proto->mux;
1803 else {
1804 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1805 ? PROTO_MODE_HTTP
1806 : PROTO_MODE_TCP);
1807
1808 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001809 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001810 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1811 status = SF_ERR_INTERNAL;
1812 goto fail_check;
1813 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001814 }
1815
Christopher Faulet61cc8522020-04-20 14:54:42 +02001816#ifdef USE_OPENSSL
1817 if (connect->sni)
1818 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001819 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001820 ssl_sock_set_servername(conn, s->check.sni);
1821
1822 if (connect->alpn)
1823 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001824 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001825 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1826#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001827 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001828 conn->send_proxy_ofs = 1;
1829 conn->flags |= CO_FL_SOCKS4;
1830 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001831 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001832 conn->send_proxy_ofs = 1;
1833 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001834 }
1835
Christopher Faulet61cc8522020-04-20 14:54:42 +02001836 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1837 conn->send_proxy_ofs = 1;
1838 conn->flags |= CO_FL_SEND_PROXY;
1839 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001840 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001841 conn->send_proxy_ofs = 1;
1842 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001843 }
1844
Christopher Faulet61cc8522020-04-20 14:54:42 +02001845 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1846 /* Some servers don't like reset on close */
1847 fdtab[cs->conn->handle.fd].linger_risk = 0;
1848 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001849
Christopher Faulet61cc8522020-04-20 14:54:42 +02001850 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1851 if (xprt_add_hs(conn) < 0)
1852 status = SF_ERR_RESOURCE;
1853 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001854
Christopher Faulet61cc8522020-04-20 14:54:42 +02001855 fail_check:
1856 /* It can return one of :
1857 * - SF_ERR_NONE if everything's OK
1858 * - SF_ERR_SRVTO if there are no more servers
1859 * - SF_ERR_SRVCL if the connection was refused by the server
1860 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1861 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1862 * - SF_ERR_INTERNAL for any other purely internal errors
1863 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1864 * Note that we try to prevent the network stack from sending the ACK during the
1865 * connect() when a pure TCP check is used (without PROXY protocol).
1866 */
1867 switch (status) {
1868 case SF_ERR_NONE:
1869 /* we allow up to min(inter, timeout.connect) for a connection
1870 * to establish but only when timeout.check is set as it may be
1871 * to short for a full check otherwise
1872 */
1873 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001874
Christopher Faulet61cc8522020-04-20 14:54:42 +02001875 if (proxy->timeout.check && proxy->timeout.connect) {
1876 int t_con = tick_add(now_ms, proxy->timeout.connect);
1877 t->expire = tick_first(t->expire, t_con);
1878 }
1879 break;
1880 case SF_ERR_SRVTO: /* ETIMEDOUT */
1881 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1882 case SF_ERR_PRXCOND:
1883 case SF_ERR_RESOURCE:
1884 case SF_ERR_INTERNAL:
1885 chk_report_conn_err(check, errno, 0);
1886 ret = TCPCHK_EVAL_STOP;
1887 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001888 }
1889
Christopher Faulet61cc8522020-04-20 14:54:42 +02001890 /* don't do anything until the connection is established */
1891 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001892 if (conn->mux) {
1893 if (next && next->action == TCPCHK_ACT_SEND)
1894 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1895 else
1896 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1897 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001898 ret = TCPCHK_EVAL_WAIT;
1899 goto out;
1900 }
1901
1902 out:
1903 if (conn && check->result == CHK_RES_FAILED)
1904 conn->flags |= CO_FL_ERROR;
1905 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001906}
1907
Christopher Faulet61cc8522020-04-20 14:54:42 +02001908/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1909 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1910 * TCPCHK_EVAL_STOP if an error occurred.
1911 */
1912static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001913{
1914 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001915 struct tcpcheck_send *send = &rule->send;
1916 struct conn_stream *cs = check->cs;
1917 struct connection *conn = cs_conn(cs);
1918 struct buffer *tmp = NULL;
1919 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001920
Christopher Faulet61cc8522020-04-20 14:54:42 +02001921 /* reset the read & write buffer */
1922 b_reset(&check->bi);
1923 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001924
Christopher Faulet61cc8522020-04-20 14:54:42 +02001925 switch (send->type) {
1926 case TCPCHK_SEND_STRING:
1927 case TCPCHK_SEND_BINARY:
1928 if (istlen(send->data) >= b_size(&check->bo)) {
1929 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1930 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1931 tcpcheck_get_step_id(check, rule));
1932 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1933 ret = TCPCHK_EVAL_STOP;
1934 goto out;
1935 }
1936 b_putist(&check->bo, send->data);
1937 break;
1938 case TCPCHK_SEND_STRING_LF:
1939 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1940 if (!b_data(&check->bo))
1941 goto out;
1942 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001943 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001944 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001945
Christopher Faulet61cc8522020-04-20 14:54:42 +02001946 tmp = alloc_trash_chunk();
1947 if (!tmp)
1948 goto error_lf;
1949 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1950 if (!b_data(tmp))
1951 goto out;
1952 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001953 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001954 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001955 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001956 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001957 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001958 case TCPCHK_SEND_HTTP: {
1959 struct htx_sl *sl;
1960 struct ist meth, uri, vsn, clen, body;
1961 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001962
Christopher Faulet61cc8522020-04-20 14:54:42 +02001963 tmp = alloc_trash_chunk();
1964 if (!tmp)
1965 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001966
Christopher Faulet61cc8522020-04-20 14:54:42 +02001967 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1968 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1969 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001970 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1971 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1972 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1973 }
1974 else
1975 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001976 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001977
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001978 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1979 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001980 slflags |= HTX_SL_F_VER_11;
1981 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1982 if (!isttest(send->http.body))
1983 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001984
Christopher Faulet61cc8522020-04-20 14:54:42 +02001985 htx = htx_from_buf(&check->bo);
1986 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1987 if (!sl)
1988 goto error_htx;
1989 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001990 if (!http_update_host(htx, sl, uri))
1991 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001992
Christopher Faulet61cc8522020-04-20 14:54:42 +02001993 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1994 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001995 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001996
Christopher Faulet61cc8522020-04-20 14:54:42 +02001997 list_for_each_entry(hdr, &send->http.hdrs, list) {
1998 chunk_reset(tmp);
1999 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2000 if (!b_data(tmp))
2001 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002002 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2003 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002004 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002005 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2006 if (!http_update_authority(htx, sl, hdr_value))
2007 goto error_htx;
2008 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002009 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002010
Christopher Faulet61cc8522020-04-20 14:54:42 +02002011 }
2012 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2013 chunk_reset(tmp);
2014 httpchk_build_status_header(check->server, tmp);
2015 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2016 goto error_htx;
2017 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002018
2019
2020 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2021 chunk_reset(tmp);
2022 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2023 body = ist2(b_orig(tmp), b_data(tmp));
2024 }
2025 else
2026 body = send->http.body;
2027 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2028
2029 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2030 !htx_add_header(htx, ist("Content-length"), clen))
2031 goto error_htx;
2032
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002033
Christopher Faulet61cc8522020-04-20 14:54:42 +02002034 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002035 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 !htx_add_endof(htx, HTX_BLK_EOM))
2037 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002038
Christopher Faulet61cc8522020-04-20 14:54:42 +02002039 htx_to_buf(htx, &check->bo);
2040 break;
2041 }
2042 case TCPCHK_SEND_UNDEF:
2043 /* Should never happen. */
2044 ret = TCPCHK_EVAL_STOP;
2045 goto out;
2046 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002047
Christopher Faulet6d471212020-04-22 11:09:25 +02002048
2049 if (conn->mux->snd_buf(cs, &check->bo,
2050 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002051 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002052 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002053 goto out;
2054 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002055 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002056 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002057 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2058 ret = TCPCHK_EVAL_WAIT;
2059 goto out;
2060 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002061
Christopher Faulet61cc8522020-04-20 14:54:42 +02002062 out:
2063 free_trash_chunk(tmp);
2064 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002065
Christopher Faulet61cc8522020-04-20 14:54:42 +02002066 error_htx:
2067 if (htx) {
2068 htx_reset(htx);
2069 htx_to_buf(htx, &check->bo);
2070 }
2071 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2072 tcpcheck_get_step_id(check, rule));
2073 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2074 ret = TCPCHK_EVAL_STOP;
2075 goto out;
2076
2077 error_lf:
2078 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2079 tcpcheck_get_step_id(check, rule));
2080 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2081 ret = TCPCHK_EVAL_STOP;
2082 goto out;
2083
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002084}
2085
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002086/* Try to receive data before evaluating a tcp-check expect rule. Returns
2087 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002088 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2089 * TCPCHK_EVAL_STOP if an error occurred.
2090 */
2091static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002092{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002093 struct conn_stream *cs = check->cs;
2094 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002095 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002096 size_t max, read, cur_read = 0;
2097 int is_empty;
2098 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002099
Christopher Faulet61cc8522020-04-20 14:54:42 +02002100 if (check->wait_list.events & SUB_RETRY_RECV)
2101 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002102
Christopher Faulet61cc8522020-04-20 14:54:42 +02002103 if (cs->flags & CS_FL_EOS)
2104 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002105
Christopher Faulet61cc8522020-04-20 14:54:42 +02002106 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002107
Christopher Faulet61cc8522020-04-20 14:54:42 +02002108 /* prepare to detect if the mux needs more room */
2109 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002110
Christopher Faulet61cc8522020-04-20 14:54:42 +02002111 while ((cs->flags & CS_FL_RCV_MORE) ||
2112 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2113 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2114 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2115 cur_read += read;
2116 if (!read ||
2117 (cs->flags & CS_FL_WANT_ROOM) ||
2118 (--read_poll <= 0) ||
2119 (read < max && read >= global.tune.recv_enough))
2120 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002121 }
2122
Christopher Faulet61cc8522020-04-20 14:54:42 +02002123 end_recv:
2124 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2125 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2126 /* Report network errors only if we got no other data. Otherwise
2127 * we'll let the upper layers decide whether the response is OK
2128 * or not. It is very common that an RST sent by the server is
2129 * reported as an error just after the last data chunk.
2130 */
2131 goto stop;
2132 }
2133 if (!cur_read) {
2134 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2135 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2136 goto wait_more_data;
2137 }
2138 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002139 int status;
2140
Christopher Faulet61cc8522020-04-20 14:54:42 +02002141 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2142 tcpcheck_get_step_id(check, rule));
2143 if (rule->comment)
2144 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002145
2146 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2147 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002148 goto stop;
2149 }
2150 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002151
2152 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002153 return ret;
2154
Christopher Faulet61cc8522020-04-20 14:54:42 +02002155 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002156 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002157 goto out;
2158
2159 wait_more_data:
2160 ret = TCPCHK_EVAL_WAIT;
2161 goto out;
2162}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002163
Christopher Faulet61cc8522020-04-20 14:54:42 +02002164/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2165 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2166 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2167 * error occurred.
2168 */
2169static enum tcpcheck_eval_ret tcpcheck_eval_expect_http(struct check *check, struct tcpcheck_rule *rule, int last_read)
Christopher Faulet267b01b2020-04-04 10:27:09 +02002170{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002171 struct htx *htx = htxbuf(&check->bi);
2172 struct htx_sl *sl;
2173 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002174 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002175 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002176 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002177 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002178 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002179 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002180
Christopher Faulet61cc8522020-04-20 14:54:42 +02002181 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002182
Christopher Faulet61cc8522020-04-20 14:54:42 +02002183 if (htx->flags & HTX_FL_PARSING_ERROR) {
2184 status = HCHK_STATUS_L7RSP;
2185 goto error;
2186 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002187
Christopher Faulet61cc8522020-04-20 14:54:42 +02002188 if (htx_is_empty(htx)) {
2189 if (last_read) {
2190 status = HCHK_STATUS_L7RSP;
2191 goto error;
2192 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002193 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002194 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002195
Christopher Faulet61cc8522020-04-20 14:54:42 +02002196 sl = http_get_stline(htx);
2197 check->code = sl->info.res.status;
2198
2199 if (check->server &&
2200 (check->server->proxy->options & PR_O_DISABLE404) &&
2201 (check->server->next_state != SRV_ST_STOPPED) &&
2202 (check->code == 404)) {
2203 /* 404 may be accepted as "stopping" only if the server was up */
2204 goto out;
2205 }
2206
2207 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2208 /* Make GCC happy ; initialize match to a failure state. */
2209 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002210 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002211
2212 switch (expect->type) {
2213 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002214 match = 0;
2215 for (i = 0; i < expect->codes.num; i++) {
2216 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2217 sl->info.res.status <= expect->codes.codes[i][1]) {
2218 match = 1;
2219 break;
2220 }
2221 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002222
2223 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002224 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002225 if (LIST_ISEMPTY(&expect->onerror_fmt))
2226 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002227 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002228 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002229 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2230
2231 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002232 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002233 if (LIST_ISEMPTY(&expect->onerror_fmt))
2234 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002235 break;
2236
Christopher Faulet39708192020-05-05 10:47:36 +02002237 case TCPCHK_EXPECT_HTTP_HEADER: {
2238 struct http_hdr_ctx ctx;
2239 struct ist npat, vpat, value;
2240 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2241
2242 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2243 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002244 if (!nbuf) {
2245 status = HCHK_STATUS_L7RSP;
2246 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002247 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002248 }
Christopher Faulet39708192020-05-05 10:47:36 +02002249 nbuf->data = sess_build_logline(check->sess, NULL, b_orig(nbuf), b_size(nbuf), &expect->hdr.name_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002250 if (!b_data(nbuf)) {
2251 status = HCHK_STATUS_L7RSP;
2252 desc = ist("log-format string evaluated to an empty string");
2253 goto error;
2254 }
Christopher Faulet39708192020-05-05 10:47:36 +02002255 npat = ist2(b_orig(nbuf), b_data(nbuf));
2256 }
2257 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2258 npat = expect->hdr.name;
2259
2260 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2261 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002262 if (!vbuf) {
2263 status = HCHK_STATUS_L7RSP;
2264 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002265 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002266 }
Christopher Faulet39708192020-05-05 10:47:36 +02002267 vbuf->data = sess_build_logline(check->sess, NULL, b_orig(vbuf), b_size(vbuf), &expect->hdr.value_fmt);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002268 if (!b_data(vbuf)) {
2269 status = HCHK_STATUS_L7RSP;
2270 desc = ist("log-format string evaluated to an empty string");
2271 goto error;
2272 }
Christopher Faulet39708192020-05-05 10:47:36 +02002273 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2274 }
2275 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2276 vpat = expect->hdr.value;
2277
2278 match = 0;
2279 ctx.blk = NULL;
2280 while (1) {
2281 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2282 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2283 if (!http_find_str_header(htx, npat, &ctx, full))
2284 goto end_of_match;
2285 break;
2286 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2287 if (!http_find_pfx_header(htx, npat, &ctx, full))
2288 goto end_of_match;
2289 break;
2290 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2291 if (!http_find_sfx_header(htx, npat, &ctx, full))
2292 goto end_of_match;
2293 break;
2294 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2295 if (!http_find_sub_header(htx, npat, &ctx, full))
2296 goto end_of_match;
2297 break;
2298 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2299 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2300 goto end_of_match;
2301 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002302 default:
2303 /* should never happen */
2304 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002305 }
2306
Christopher Faulet083eff32020-05-07 15:41:39 +02002307 /* A header has matched the name pattern, let's test its
2308 * value now (always defined from there). If there is no
2309 * value pattern, it is a good match.
2310 */
2311
Christopher Faulet39708192020-05-05 10:47:36 +02002312 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2313 match = 1;
2314 goto end_of_match;
2315 }
2316
2317 value = ctx.value;
2318 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2319 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2320 if (isteq(value, vpat)) {
2321 match = 1;
2322 goto end_of_match;
2323 }
2324 break;
2325 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2326 if (istlen(value) < istlen(vpat))
2327 break;
2328 value = ist2(istptr(value), istlen(vpat));
2329 if (isteq(value, vpat)) {
2330 match = 1;
2331 goto end_of_match;
2332 }
2333 break;
2334 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2335 if (istlen(value) < istlen(vpat))
2336 break;
2337 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2338 if (isteq(value, vpat)) {
2339 match = 1;
2340 goto end_of_match;
2341 }
2342 break;
2343 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2344 if (isttest(istist(value, vpat))) {
2345 match = 1;
2346 goto end_of_match;
2347 }
2348 break;
2349 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2350 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2351 match = 1;
2352 goto end_of_match;
2353 }
2354 break;
2355 }
2356 }
2357
2358 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002359 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002360 if (LIST_ISEMPTY(&expect->onerror_fmt))
2361 desc = htx_sl_res_reason(sl);
2362 break;
2363 }
2364
Christopher Faulet61cc8522020-04-20 14:54:42 +02002365 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002366 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002367 case TCPCHK_EXPECT_HTTP_BODY_LF:
2368 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002369 chunk_reset(&trash);
2370 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2371 enum htx_blk_type type = htx_get_blk_type(blk);
2372
2373 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2374 break;
2375 if (type == HTX_BLK_DATA) {
2376 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2377 break;
2378 }
2379 }
2380
2381 if (!b_data(&trash)) {
2382 if (!last_read)
2383 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002384 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002385 if (LIST_ISEMPTY(&expect->onerror_fmt))
2386 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002387 goto error;
2388 }
2389
Christopher Fauletaaab0832020-05-05 15:54:22 +02002390 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2391 tmp = alloc_trash_chunk();
2392 if (!tmp) {
2393 status = HCHK_STATUS_L7RSP;
2394 desc = ist("Failed to allocate buffer to eval log-format string");
2395 goto error;
2396 }
2397 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2398 if (!b_data(tmp)) {
2399 status = HCHK_STATUS_L7RSP;
2400 desc = ist("log-format string evaluated to an empty string");
2401 goto error;
2402 }
2403 }
2404
Christopher Faulet61cc8522020-04-20 14:54:42 +02002405 if (!last_read &&
2406 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002407 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002408 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2409 ret = TCPCHK_EVAL_WAIT;
2410 goto out;
2411 }
2412
2413 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002414 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002415 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2416 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002417 else
2418 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2419
2420 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002421 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002422 if (LIST_ISEMPTY(&expect->onerror_fmt))
2423 desc = (inverse
2424 ? ist("HTTP check matched unwanted content")
2425 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002426 break;
2427
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002428
Christopher Faulet61cc8522020-04-20 14:54:42 +02002429 default:
2430 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002431 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002432 goto error;
2433 }
2434
Christopher Faulet61cc8522020-04-20 14:54:42 +02002435 /* Wait for more data on mismatch only if no minimum is defined (-1),
2436 * otherwise the absence of match is already conclusive.
2437 */
2438 if (!match && !last_read && (expect->min_recv == -1)) {
2439 ret = TCPCHK_EVAL_WAIT;
2440 goto out;
2441 }
2442
2443 if (!(match ^ inverse))
2444 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002445
2446 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002447 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002448 free_trash_chunk(nbuf);
2449 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002450 free_trash_chunk(msg);
2451 return ret;
2452
2453 error:
2454 ret = TCPCHK_EVAL_STOP;
2455 msg = alloc_trash_chunk();
2456 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002457 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002458 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2459 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002460
2461 wait_more_data:
2462 ret = TCPCHK_EVAL_WAIT;
2463 goto out;
2464}
2465
Christopher Faulet61cc8522020-04-20 14:54:42 +02002466/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2467 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2468 * if an error occurred.
2469 */
2470static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2471{
2472 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2473 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002474 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002475 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002476 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002477 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002478
Christopher Faulet61cc8522020-04-20 14:54:42 +02002479 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002480
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 /* The current expect might need more data than the previous one, check again
2482 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002483 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002484 if (!last_read) {
2485 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2486 (b_data(&check->bi) < istlen(expect->data))) {
2487 ret = TCPCHK_EVAL_WAIT;
2488 goto out;
2489 }
2490 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2491 ret = TCPCHK_EVAL_WAIT;
2492 goto out;
2493 }
2494 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002495
Christopher Faulet61cc8522020-04-20 14:54:42 +02002496 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2497 /* Make GCC happy ; initialize match to a failure state. */
2498 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002499 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002500
Christopher Faulet61cc8522020-04-20 14:54:42 +02002501 switch (expect->type) {
2502 case TCPCHK_EXPECT_STRING:
2503 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002504 match = my_memmem(b_head(&check->bi), b_data(&check->bi), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002505 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002506 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002507 match = regex_exec2(expect->regex, b_head(&check->bi), MIN(b_data(&check->bi), b_size(&check->bi)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002508 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002509
Christopher Faulet67a23452020-05-05 18:10:01 +02002510 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002511 chunk_reset(&trash);
2512 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002513 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002514 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002515
2516 case TCPCHK_EXPECT_STRING_LF:
2517 case TCPCHK_EXPECT_BINARY_LF:
2518 match = 0;
2519 tmp = alloc_trash_chunk();
2520 if (!tmp) {
2521 status = HCHK_STATUS_L7RSP;
2522 desc = ist("Failed to allocate buffer to eval format string");
2523 goto error;
2524 }
2525 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2526 if (!b_data(tmp)) {
2527 status = HCHK_STATUS_L7RSP;
2528 desc = ist("log-format string evaluated to an empty string");
2529 goto error;
2530 }
2531 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2532 int len = tmp->data;
2533 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2534 status = HCHK_STATUS_L7RSP;
2535 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2536 goto error;
2537 }
2538 tmp->data = len;
2539 }
2540 if (b_data(&check->bi) < tmp->data) {
2541 if (!last_read) {
2542 ret = TCPCHK_EVAL_WAIT;
2543 goto out;
2544 }
2545 break;
2546 }
2547 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2548 break;
2549
Christopher Faulet61cc8522020-04-20 14:54:42 +02002550 case TCPCHK_EXPECT_CUSTOM:
2551 if (expect->custom)
2552 ret = expect->custom(check, rule, last_read);
2553 goto out;
2554 default:
2555 /* Should never happen. */
2556 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002557 goto out;
2558 }
2559
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002560
Christopher Faulet61cc8522020-04-20 14:54:42 +02002561 /* Wait for more data on mismatch only if no minimum is defined (-1),
2562 * otherwise the absence of match is already conclusive.
2563 */
2564 if (!match && !last_read && (expect->min_recv == -1)) {
2565 ret = TCPCHK_EVAL_WAIT;
2566 goto out;
2567 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002568
Christopher Faulet61cc8522020-04-20 14:54:42 +02002569 /* Result as expected, next rule. */
2570 if (match ^ inverse)
2571 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002572
Christopher Fauletaaab0832020-05-05 15:54:22 +02002573 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002574 /* From this point on, we matched something we did not want, this is an error state. */
2575 ret = TCPCHK_EVAL_STOP;
2576 msg = alloc_trash_chunk();
2577 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002578 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002579 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002580 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002581
Christopher Faulet61cc8522020-04-20 14:54:42 +02002582 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002583 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002584 return ret;
2585}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002586
Christopher Faulet61cc8522020-04-20 14:54:42 +02002587/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002588 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002589 * waits.
2590 */
2591static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2592{
2593 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2594 struct act_rule *act_rule;
2595 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002596
Christopher Faulet61cc8522020-04-20 14:54:42 +02002597 act_rule =rule->action_kw.rule;
2598 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2599 if (act_ret != ACT_RET_CONT) {
2600 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2601 tcpcheck_get_step_id(check, rule));
2602 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2603 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002604 }
2605
Christopher Faulet61cc8522020-04-20 14:54:42 +02002606 return ret;
2607}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002608
Christopher Faulet61cc8522020-04-20 14:54:42 +02002609/* Executes a tcp-check ruleset. Note that this is called both from the
2610 * connection's wake() callback and from the check scheduling task. It returns
2611 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2612 * presenting the risk of an fd replacement.
2613 *
2614 * Please do NOT place any return statement in this function and only leave
2615 * via the out_end_tcpcheck label after setting retcode.
2616 */
2617static int tcpcheck_main(struct check *check)
2618{
2619 struct tcpcheck_rule *rule;
2620 struct conn_stream *cs = check->cs;
2621 struct connection *conn = cs_conn(cs);
2622 int must_read = 1, last_read = 0;
2623 int ret, retcode = 0;
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002624 enum tcpcheck_eval_ret eval_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002625
Christopher Faulet61cc8522020-04-20 14:54:42 +02002626 /* here, we know that the check is complete or that it failed */
2627 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002628 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002629
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002630 /* Note: the conn-stream and the connection may only be undefined before
2631 * the first rule evaluation (it is always a connect rule) or when the
2632 * conn-stream allocation failed on the first connect.
2633 */
2634
Christopher Faulet61cc8522020-04-20 14:54:42 +02002635 /* 1- check for connection error, if any */
2636 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2637 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002638
Christopher Faulet61cc8522020-04-20 14:54:42 +02002639 /* 2- check if we are waiting for the connection establishment. It only
2640 * happens during TCPCHK_ACT_CONNECT. */
2641 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002642 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002643 struct tcpcheck_rule *next;
2644
2645 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2646 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002647 if (!(check->wait_list.events & SUB_RETRY_SEND))
2648 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002649 goto out;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002650 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002651 else {
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002652 eval_ret = tcpcheck_eval_recv(check, check->current_step);
2653 if (eval_ret == TCPCHK_EVAL_STOP)
2654 goto out_end_tcpcheck;
2655 else if (eval_ret == TCPCHK_EVAL_WAIT)
2656 goto out;
2657 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2658 must_read = 0;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002659 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002660 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002661 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002662 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002663
2664 /* 3- check for pending outgoing data. It only happens during
2665 * TCPCHK_ACT_SEND. */
2666 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002667 if (b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002668 /* We're already waiting to be able to send, give up */
2669 if (check->wait_list.events & SUB_RETRY_SEND)
2670 goto out;
2671
Christopher Faulet6d471212020-04-22 11:09:25 +02002672 ret = conn->mux->snd_buf(cs, &check->bo,
2673 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002674 if (ret <= 0) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002675 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002676 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002677 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002678 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002679 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002680 goto out;
2681 }
2682 }
2683 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002684 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002685
Christopher Faulet61cc8522020-04-20 14:54:42 +02002686 /* 4- check if a rule must be resume. It happens if check->current_step
2687 * is defined. */
2688 else if (check->current_step)
2689 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002690
Christopher Faulet61cc8522020-04-20 14:54:42 +02002691 /* 5- It is the first evaluation. We must create a session and preset
2692 * tcp-check variables */
2693 else {
2694 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002695
Christopher Faulet61cc8522020-04-20 14:54:42 +02002696 /* First evaluation, create a session */
2697 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2698 if (!check->sess) {
2699 chunk_printf(&trash, "TCPCHK error allocating check session");
2700 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2701 goto out_end_tcpcheck;
2702 }
2703 vars_init(&check->vars, SCOPE_CHECK);
2704 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002705
Christopher Faulet61cc8522020-04-20 14:54:42 +02002706 /* Preset tcp-check variables */
2707 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2708 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002709
Christopher Faulet61cc8522020-04-20 14:54:42 +02002710 memset(&smp, 0, sizeof(smp));
2711 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2712 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002713 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002714 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002715 }
2716
Christopher Faulet61cc8522020-04-20 14:54:42 +02002717 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002718
Christopher Faulet61cc8522020-04-20 14:54:42 +02002719 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002720 check->code = 0;
2721 switch (rule->action) {
2722 case TCPCHK_ACT_CONNECT:
2723 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002724
Christopher Faulet61cc8522020-04-20 14:54:42 +02002725 /* close but not release yet previous connection */
2726 if (check->cs) {
2727 cs_close(check->cs);
2728 retcode = -1; /* do not reuse the fd in the caller! */
2729 }
2730 eval_ret = tcpcheck_eval_connect(check, rule);
Christopher Faulet3cbdd222020-05-26 11:14:50 +02002731
2732 /* Refresh conn-stream and connection */
2733 cs = check->cs;
2734 conn = cs_conn(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002735 must_read = 1; last_read = 0;
2736 break;
2737 case TCPCHK_ACT_SEND:
2738 check->current_step = rule;
2739 eval_ret = tcpcheck_eval_send(check, rule);
2740 must_read = 1;
2741 break;
2742 case TCPCHK_ACT_EXPECT:
2743 check->current_step = rule;
2744 if (must_read) {
2745 if (check->proxy->timeout.check)
2746 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002747
Christopher Faulet61cc8522020-04-20 14:54:42 +02002748 eval_ret = tcpcheck_eval_recv(check, rule);
2749 if (eval_ret == TCPCHK_EVAL_STOP)
2750 goto out_end_tcpcheck;
2751 else if (eval_ret == TCPCHK_EVAL_WAIT)
2752 goto out;
2753 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2754 must_read = 0;
2755 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002756
Christopher Faulet61cc8522020-04-20 14:54:42 +02002757 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2758 ? tcpcheck_eval_expect_http(check, rule, last_read)
2759 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002760
Christopher Faulet61cc8522020-04-20 14:54:42 +02002761 if (eval_ret == TCPCHK_EVAL_WAIT) {
2762 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002763 if (!(check->wait_list.events & SUB_RETRY_RECV))
2764 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002765 }
2766 break;
2767 case TCPCHK_ACT_ACTION_KW:
2768 /* Don't update the current step */
2769 eval_ret = tcpcheck_eval_action_kw(check, rule);
2770 break;
2771 default:
2772 /* Otherwise, just go to the next one and don't update
2773 * the current step
2774 */
2775 eval_ret = TCPCHK_EVAL_CONTINUE;
2776 break;
2777 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002778
Christopher Faulet61cc8522020-04-20 14:54:42 +02002779 switch (eval_ret) {
2780 case TCPCHK_EVAL_CONTINUE:
2781 break;
2782 case TCPCHK_EVAL_WAIT:
2783 goto out;
2784 case TCPCHK_EVAL_STOP:
2785 goto out_end_tcpcheck;
2786 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002787 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002788
Christopher Faulet61cc8522020-04-20 14:54:42 +02002789 /* All rules was evaluated */
2790 if (check->current_step) {
2791 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002792
Christopher Faulet61cc8522020-04-20 14:54:42 +02002793 if (rule->action == TCPCHK_ACT_EXPECT) {
2794 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002795 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002796
Christopher Faulet61cc8522020-04-20 14:54:42 +02002797 if (check->server &&
2798 (check->server->proxy->options & PR_O_DISABLE404) &&
2799 (check->server->next_state != SRV_ST_STOPPED) &&
2800 (check->code == 404)) {
2801 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2802 goto out_end_tcpcheck;
2803 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002804
Christopher Faulet61cc8522020-04-20 14:54:42 +02002805 msg = alloc_trash_chunk();
2806 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002807 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002808 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2809 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002810 free_trash_chunk(msg);
2811 }
2812 else if (rule->action == TCPCHK_ACT_CONNECT) {
2813 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002814 enum healthcheck_status status = HCHK_STATUS_L4OK;
2815#ifdef USE_OPENSSL
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002816 if (ssl_sock_is_ssl(conn))
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002817 status = HCHK_STATUS_L6OK;
2818#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002819 set_server_check_status(check, status, msg);
2820 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002821 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002822 else
2823 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002824
Christopher Faulet61cc8522020-04-20 14:54:42 +02002825 out_end_tcpcheck:
2826 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2827 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002828
Christopher Faulet61cc8522020-04-20 14:54:42 +02002829 out:
2830 return retcode;
2831}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002832
Christopher Faulet14cd3162020-04-16 14:50:06 +02002833
Christopher Faulet61cc8522020-04-20 14:54:42 +02002834/**************************************************************************/
2835/************** Health-checks based on an external process ****************/
2836/**************************************************************************/
2837static struct list pid_list = LIST_HEAD_INIT(pid_list);
2838static struct pool_head *pool_head_pid_list;
2839__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002840
Christopher Faulet61cc8522020-04-20 14:54:42 +02002841struct extcheck_env {
2842 char *name; /* environment variable name */
2843 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2844};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002845
Christopher Faulet61cc8522020-04-20 14:54:42 +02002846/* environment variables memory requirement for different types of data */
2847#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2848 * such environment variables are not updatable. */
2849#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2850#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2851#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002852
Christopher Faulet61cc8522020-04-20 14:54:42 +02002853/* external checks environment variables */
2854enum {
2855 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002856
Christopher Faulet61cc8522020-04-20 14:54:42 +02002857 /* Proxy specific environment variables */
2858 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2859 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2860 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2861 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002862
Christopher Faulet61cc8522020-04-20 14:54:42 +02002863 /* Server specific environment variables */
2864 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2865 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2866 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2867 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2868 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2869 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002870
Christopher Faulet61cc8522020-04-20 14:54:42 +02002871 EXTCHK_SIZE
2872};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002873
Christopher Faulet61cc8522020-04-20 14:54:42 +02002874const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2875 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2876 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2877 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2878 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2879 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2880 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2881 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2882 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2883 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2884 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2885 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2886};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002887
Christopher Faulet61cc8522020-04-20 14:54:42 +02002888void block_sigchld(void)
2889{
2890 sigset_t set;
2891 sigemptyset(&set);
2892 sigaddset(&set, SIGCHLD);
2893 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2894}
Willy Tarreaube373152018-09-06 11:45:30 +02002895
Christopher Faulet61cc8522020-04-20 14:54:42 +02002896void unblock_sigchld(void)
2897{
2898 sigset_t set;
2899 sigemptyset(&set);
2900 sigaddset(&set, SIGCHLD);
2901 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002902}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002903
Christopher Faulet61cc8522020-04-20 14:54:42 +02002904static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002905{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002906 struct pid_list *elem;
2907 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002908
Christopher Faulet61cc8522020-04-20 14:54:42 +02002909 elem = pool_alloc(pool_head_pid_list);
2910 if (!elem)
2911 return NULL;
2912 elem->pid = pid;
2913 elem->t = t;
2914 elem->exited = 0;
2915 check->curpid = elem;
2916 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002917
Christopher Faulet61cc8522020-04-20 14:54:42 +02002918 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2919 LIST_ADD(&pid_list, &elem->list);
2920 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002921
Christopher Faulet61cc8522020-04-20 14:54:42 +02002922 return elem;
2923}
Christopher Faulete5870d82020-04-15 11:32:03 +02002924
Christopher Faulet61cc8522020-04-20 14:54:42 +02002925static void pid_list_del(struct pid_list *elem)
2926{
2927 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002928
Christopher Faulet61cc8522020-04-20 14:54:42 +02002929 if (!elem)
2930 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002931
Christopher Faulet61cc8522020-04-20 14:54:42 +02002932 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2933 LIST_DEL(&elem->list);
2934 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002935
Christopher Faulet61cc8522020-04-20 14:54:42 +02002936 if (!elem->exited)
2937 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002938
Christopher Faulet61cc8522020-04-20 14:54:42 +02002939 check = elem->t->context;
2940 check->curpid = NULL;
2941 pool_free(pool_head_pid_list, elem);
2942}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002943
Christopher Faulet61cc8522020-04-20 14:54:42 +02002944/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2945static void pid_list_expire(pid_t pid, int status)
2946{
2947 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002948
Christopher Faulet61cc8522020-04-20 14:54:42 +02002949 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2950 list_for_each_entry(elem, &pid_list, list) {
2951 if (elem->pid == pid) {
2952 elem->t->expire = now_ms;
2953 elem->status = status;
2954 elem->exited = 1;
2955 task_wakeup(elem->t, TASK_WOKEN_IO);
2956 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002957 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002958 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002959 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2960}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002961
Christopher Faulet61cc8522020-04-20 14:54:42 +02002962static void sigchld_handler(struct sig_handler *sh)
2963{
2964 pid_t pid;
2965 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002966
Christopher Faulet61cc8522020-04-20 14:54:42 +02002967 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2968 pid_list_expire(pid, status);
2969}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002970
Christopher Faulet61cc8522020-04-20 14:54:42 +02002971static int init_pid_list(void)
2972{
2973 if (pool_head_pid_list != NULL)
2974 /* Nothing to do */
2975 return 0;
2976
2977 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2978 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2979 strerror(errno));
2980 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002981 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002982
Christopher Faulet61cc8522020-04-20 14:54:42 +02002983 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2984 if (pool_head_pid_list == NULL) {
2985 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2986 strerror(errno));
2987 return 1;
2988 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002989
Christopher Faulet61cc8522020-04-20 14:54:42 +02002990 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002991}
2992
Christopher Faulet61cc8522020-04-20 14:54:42 +02002993/* helper macro to set an environment variable and jump to a specific label on failure. */
2994#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002995
Christopher Faulet61cc8522020-04-20 14:54:42 +02002996/*
2997 * helper function to allocate enough memory to store an environment variable.
2998 * It will also check that the environment variable is updatable, and silently
2999 * fail if not.
3000 */
3001static int extchk_setenv(struct check *check, int idx, const char *value)
3002{
3003 int len, ret;
3004 char *envname;
3005 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003006
Christopher Faulet61cc8522020-04-20 14:54:42 +02003007 if (idx < 0 || idx >= EXTCHK_SIZE) {
3008 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
3009 return 1;
3010 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003011
Christopher Faulet61cc8522020-04-20 14:54:42 +02003012 envname = extcheck_envs[idx].name;
3013 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003014
Christopher Faulet61cc8522020-04-20 14:54:42 +02003015 /* Check if the environment variable is already set, and silently reject
3016 * the update if this one is not updatable. */
3017 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3018 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003019
Christopher Faulet61cc8522020-04-20 14:54:42 +02003020 /* Instead of sending NOT_USED, sending an empty value is preferable */
3021 if (strcmp(value, "NOT_USED") == 0) {
3022 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003023 }
3024
Christopher Faulet61cc8522020-04-20 14:54:42 +02003025 len = strlen(envname) + 1;
3026 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3027 len += strlen(value);
3028 else
3029 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003030
Christopher Faulet61cc8522020-04-20 14:54:42 +02003031 if (!check->envp[idx])
3032 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003033
Christopher Faulet61cc8522020-04-20 14:54:42 +02003034 if (!check->envp[idx]) {
3035 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3036 return 1;
3037 }
3038 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3039 if (ret < 0) {
3040 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3041 return 1;
3042 }
3043 else if (ret > len) {
3044 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3045 return 1;
3046 }
3047 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003048}
3049
Christopher Faulet61cc8522020-04-20 14:54:42 +02003050static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003051{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003052 struct server *s = check->server;
3053 struct proxy *px = s->proxy;
3054 struct listener *listener = NULL, *l;
3055 int i;
3056 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3057 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003058
Christopher Faulet61cc8522020-04-20 14:54:42 +02003059 list_for_each_entry(l, &px->conf.listeners, by_fe)
3060 /* Use the first INET, INET6 or UNIX listener */
3061 if (l->addr.ss_family == AF_INET ||
3062 l->addr.ss_family == AF_INET6 ||
3063 l->addr.ss_family == AF_UNIX) {
3064 listener = l;
3065 break;
3066 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003067
Christopher Faulet61cc8522020-04-20 14:54:42 +02003068 check->curpid = NULL;
3069 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3070 if (!check->envp) {
3071 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3072 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003073 }
3074
Christopher Faulet61cc8522020-04-20 14:54:42 +02003075 check->argv = calloc(6, sizeof(char *));
3076 if (!check->argv) {
3077 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3078 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003079 }
3080
Christopher Faulet61cc8522020-04-20 14:54:42 +02003081 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003082
Christopher Faulet61cc8522020-04-20 14:54:42 +02003083 if (!listener) {
3084 check->argv[1] = strdup("NOT_USED");
3085 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003086 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003087 else if (listener->addr.ss_family == AF_INET ||
3088 listener->addr.ss_family == AF_INET6) {
3089 addr_to_str(&listener->addr, buf, sizeof(buf));
3090 check->argv[1] = strdup(buf);
3091 port_to_str(&listener->addr, buf, sizeof(buf));
3092 check->argv[2] = strdup(buf);
3093 }
3094 else if (listener->addr.ss_family == AF_UNIX) {
3095 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003096
Christopher Faulet61cc8522020-04-20 14:54:42 +02003097 un = (struct sockaddr_un *)&listener->addr;
3098 check->argv[1] = strdup(un->sun_path);
3099 check->argv[2] = strdup("NOT_USED");
3100 }
3101 else {
3102 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3103 goto err;
3104 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003105
Christopher Faulet61cc8522020-04-20 14:54:42 +02003106 if (!check->argv[1] || !check->argv[2]) {
3107 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3108 goto err;
3109 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003110
Christopher Faulet61cc8522020-04-20 14:54:42 +02003111 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3112 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3113 if (!check->argv[3] || !check->argv[4]) {
3114 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3115 goto err;
3116 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003117
Christopher Faulet61cc8522020-04-20 14:54:42 +02003118 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3119 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3120 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003121
Christopher Faulet61cc8522020-04-20 14:54:42 +02003122 for (i = 0; i < 5; i++) {
3123 if (!check->argv[i]) {
3124 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3125 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003126 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003127 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003128
Christopher Faulet61cc8522020-04-20 14:54:42 +02003129 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3130 /* Add proxy environment variables */
3131 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3132 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3133 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3134 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3135 /* Add server environment variables */
3136 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3137 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3138 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3139 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3140 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3141 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003142
Christopher Faulet61cc8522020-04-20 14:54:42 +02003143 /* Ensure that we don't leave any hole in check->envp */
3144 for (i = 0; i < EXTCHK_SIZE; i++)
3145 if (!check->envp[i])
3146 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003147
Christopher Faulet61cc8522020-04-20 14:54:42 +02003148 return 1;
3149err:
3150 if (check->envp) {
3151 for (i = 0; i < EXTCHK_SIZE; i++)
3152 free(check->envp[i]);
3153 free(check->envp);
3154 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003155 }
3156
Christopher Faulet61cc8522020-04-20 14:54:42 +02003157 if (check->argv) {
3158 for (i = 1; i < 5; i++)
3159 free(check->argv[i]);
3160 free(check->argv);
3161 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003162 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003163 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003164}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003165
Christopher Faulet61cc8522020-04-20 14:54:42 +02003166/*
3167 * establish a server health-check that makes use of a process.
3168 *
3169 * It can return one of :
3170 * - SF_ERR_NONE if everything's OK
3171 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3172 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3173 *
3174 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003175 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003176static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003177{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003178 char buf[256];
3179 struct check *check = t->context;
3180 struct server *s = check->server;
3181 struct proxy *px = s->proxy;
3182 int status;
3183 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003184
Christopher Faulet61cc8522020-04-20 14:54:42 +02003185 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003186
Christopher Faulet61cc8522020-04-20 14:54:42 +02003187 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003188
Christopher Faulet61cc8522020-04-20 14:54:42 +02003189 pid = fork();
3190 if (pid < 0) {
3191 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3192 (global.tune.options & GTUNE_INSECURE_FORK) ?
3193 "" : " (likely caused by missing 'insecure-fork-wanted')",
3194 strerror(errno));
3195 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003196 goto out;
3197 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003198 if (pid == 0) {
3199 /* Child */
3200 extern char **environ;
3201 struct rlimit limit;
3202 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003203
Christopher Faulet61cc8522020-04-20 14:54:42 +02003204 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3205 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003206
Christopher Faulet61cc8522020-04-20 14:54:42 +02003207 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003208
Christopher Faulet61cc8522020-04-20 14:54:42 +02003209 /* restore the initial FD limits */
3210 limit.rlim_cur = rlim_fd_cur_at_boot;
3211 limit.rlim_max = rlim_fd_max_at_boot;
3212 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3213 getrlimit(RLIMIT_NOFILE, &limit);
3214 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3215 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3216 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3217 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003218
Christopher Faulet61cc8522020-04-20 14:54:42 +02003219 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003220
Christopher Faulet61cc8522020-04-20 14:54:42 +02003221 /* Update some environment variables and command args: curconn, server addr and server port */
3222 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003223
Christopher Faulet61cc8522020-04-20 14:54:42 +02003224 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3225 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003226
Christopher Faulet61cc8522020-04-20 14:54:42 +02003227 *check->argv[4] = 0;
3228 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3229 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3230 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003231
Christopher Faulet61cc8522020-04-20 14:54:42 +02003232 haproxy_unblock_signals();
3233 execvp(px->check_command, check->argv);
3234 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3235 strerror(errno));
3236 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003237 }
3238
Christopher Faulet61cc8522020-04-20 14:54:42 +02003239 /* Parent */
3240 if (check->result == CHK_RES_UNKNOWN) {
3241 if (pid_list_add(pid, t) != NULL) {
3242 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3243
3244 if (px->timeout.check && px->timeout.connect) {
3245 int t_con = tick_add(now_ms, px->timeout.connect);
3246 t->expire = tick_first(t->expire, t_con);
3247 }
3248 status = SF_ERR_NONE;
3249 goto out;
3250 }
3251 else {
3252 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3253 }
3254 kill(pid, SIGTERM); /* process creation error */
3255 }
3256 else
3257 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3258
3259out:
3260 unblock_sigchld();
3261 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003262}
3263
Christopher Faulet61cc8522020-04-20 14:54:42 +02003264/*
3265 * manages a server health-check that uses an external process. Returns
3266 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003267 *
3268 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003269 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003270 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003271static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003272{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003273 struct check *check = context;
3274 struct server *s = check->server;
3275 int rv;
3276 int ret;
3277 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003278
Christopher Faulet61cc8522020-04-20 14:54:42 +02003279 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3280 if (!(check->state & CHK_ST_INPROGRESS)) {
3281 /* no check currently running */
3282 if (!expired) /* woke up too early */
3283 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003284
Christopher Faulet61cc8522020-04-20 14:54:42 +02003285 /* we don't send any health-checks when the proxy is
3286 * stopped, the server should not be checked or the check
3287 * is disabled.
3288 */
3289 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3290 s->proxy->state == PR_STSTOPPED)
3291 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003292
Christopher Faulet61cc8522020-04-20 14:54:42 +02003293 /* we'll initiate a new check */
3294 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003295
Christopher Faulet61cc8522020-04-20 14:54:42 +02003296 check->state |= CHK_ST_INPROGRESS;
3297
3298 ret = connect_proc_chk(t);
3299 if (ret == SF_ERR_NONE) {
3300 /* the process was forked, we allow up to min(inter,
3301 * timeout.connect) for it to report its status, but
3302 * only when timeout.check is set as it may be to short
3303 * for a full check otherwise.
3304 */
3305 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3306
3307 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3308 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3309 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003310 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003311 task_set_affinity(t, tid_bit);
3312 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003313 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003314
Christopher Faulet61cc8522020-04-20 14:54:42 +02003315 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003316
Christopher Faulet61cc8522020-04-20 14:54:42 +02003317 check->state &= ~CHK_ST_INPROGRESS;
3318 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003319
Christopher Faulet61cc8522020-04-20 14:54:42 +02003320 /* we allow up to min(inter, timeout.connect) for a connection
3321 * to establish but only when timeout.check is set
3322 * as it may be to short for a full check otherwise
3323 */
3324 while (tick_is_expired(t->expire, now_ms)) {
3325 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003326
Christopher Faulet61cc8522020-04-20 14:54:42 +02003327 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3328 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003329
Christopher Faulet61cc8522020-04-20 14:54:42 +02003330 if (s->proxy->timeout.check)
3331 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003332 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003333 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003334 else {
3335 /* there was a test running.
3336 * First, let's check whether there was an uncaught error,
3337 * which can happen on connect timeout or error.
3338 */
3339 if (check->result == CHK_RES_UNKNOWN) {
3340 /* good connection is enough for pure TCP check */
3341 struct pid_list *elem = check->curpid;
3342 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003343
Christopher Faulet61cc8522020-04-20 14:54:42 +02003344 if (elem->exited) {
3345 status = elem->status; /* Save in case the process exits between use below */
3346 if (!WIFEXITED(status))
3347 check->code = -1;
3348 else
3349 check->code = WEXITSTATUS(status);
3350 if (!WIFEXITED(status) || WEXITSTATUS(status))
3351 status = HCHK_STATUS_PROCERR;
3352 else
3353 status = HCHK_STATUS_PROCOK;
3354 } else if (expired) {
3355 status = HCHK_STATUS_PROCTOUT;
3356 ha_warning("kill %d\n", (int)elem->pid);
3357 kill(elem->pid, SIGTERM);
3358 }
3359 set_server_check_status(check, status, NULL);
3360 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003361
Christopher Faulet61cc8522020-04-20 14:54:42 +02003362 if (check->result == CHK_RES_FAILED) {
3363 /* a failure or timeout detected */
3364 check_notify_failure(check);
3365 }
3366 else if (check->result == CHK_RES_CONDPASS) {
3367 /* check is OK but asks for stopping mode */
3368 check_notify_stopping(check);
3369 }
3370 else if (check->result == CHK_RES_PASSED) {
3371 /* a success was detected */
3372 check_notify_success(check);
3373 }
3374 task_set_affinity(t, 1);
3375 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003376
Christopher Faulet61cc8522020-04-20 14:54:42 +02003377 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003378
Christopher Faulet61cc8522020-04-20 14:54:42 +02003379 rv = 0;
3380 if (global.spread_checks > 0) {
3381 rv = srv_getinter(check) * global.spread_checks / 100;
3382 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3383 }
3384 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3385 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003386
Christopher Faulet61cc8522020-04-20 14:54:42 +02003387 reschedule:
3388 while (tick_is_expired(t->expire, now_ms))
3389 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003390
Christopher Faulet61cc8522020-04-20 14:54:42 +02003391 out_unlock:
3392 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3393 return t;
3394}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003395
Baptiste Assmann248f1172018-03-01 21:49:01 +01003396
Christopher Faulet61cc8522020-04-20 14:54:42 +02003397/**************************************************************************/
3398/***************** Health-checks based on connections *********************/
3399/**************************************************************************/
3400/* This function is used only for server health-checks. It handles connection
3401 * status updates including errors. If necessary, it wakes the check task up.
3402 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3403 * connection (eg: reconnect). It relies on tcpcheck_main().
3404 */
3405static int wake_srv_chk(struct conn_stream *cs)
3406{
3407 struct connection *conn = cs->conn;
3408 struct check *check = cs->data;
3409 struct email_alertq *q = container_of(check, typeof(*q), check);
3410 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003411
Christopher Faulet61cc8522020-04-20 14:54:42 +02003412 if (check->server)
3413 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3414 else
3415 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003416
Christopher Faulet61cc8522020-04-20 14:54:42 +02003417 /* we may have to make progress on the TCP checks */
3418 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003419
Christopher Faulet61cc8522020-04-20 14:54:42 +02003420 cs = check->cs;
3421 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003422
Christopher Faulet61cc8522020-04-20 14:54:42 +02003423 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3424 /* We may get error reports bypassing the I/O handlers, typically
3425 * the case when sending a pure TCP check which fails, then the I/O
3426 * handlers above are not called. This is completely handled by the
3427 * main processing task so let's simply wake it up. If we get here,
3428 * we expect errno to still be valid.
3429 */
3430 chk_report_conn_err(check, errno, 0);
3431 task_wakeup(check->task, TASK_WOKEN_IO);
3432 }
3433
3434 if (check->result != CHK_RES_UNKNOWN) {
3435 /* Check complete or aborted. If connection not yet closed do it
3436 * now and wake the check task up to be sure the result is
3437 * handled ASAP. */
3438 conn_sock_drain(conn);
3439 cs_close(cs);
3440 ret = -1;
3441 /* We may have been scheduled to run, and the
3442 * I/O handler expects to have a cs, so remove
3443 * the tasklet
3444 */
3445 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3446 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003447 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003448
3449 if (check->server)
3450 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003451 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003452 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003453
Christopher Faulet61cc8522020-04-20 14:54:42 +02003454 /* if a connection got replaced, we must absolutely prevent the connection
3455 * handler from touching its fd, and perform the FD polling updates ourselves
3456 */
3457 if (ret < 0)
3458 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003459
Christopher Faulet61cc8522020-04-20 14:54:42 +02003460 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003461}
3462
Christopher Faulet61cc8522020-04-20 14:54:42 +02003463/* This function checks if any I/O is wanted, and if so, attempts to do so */
3464static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003465{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003466 struct check *check = ctx;
3467 struct conn_stream *cs = check->cs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003468
Christopher Faulet3d5e1212020-05-28 14:34:02 +02003469 wake_srv_chk(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003470 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003471}
3472
Christopher Faulet61cc8522020-04-20 14:54:42 +02003473/* manages a server health-check that uses a connection. Returns
3474 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3475 *
3476 * Please do NOT place any return statement in this function and only leave
3477 * via the out_unlock label.
3478 */
3479static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003480{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003481 struct check *check = context;
3482 struct proxy *proxy = check->proxy;
3483 struct conn_stream *cs = check->cs;
3484 struct connection *conn = cs_conn(cs);
3485 int rv;
3486 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003487
Christopher Faulet61cc8522020-04-20 14:54:42 +02003488 if (check->server)
3489 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3490 if (!(check->state & CHK_ST_INPROGRESS)) {
3491 /* no check currently running */
3492 if (!expired) /* woke up too early */
3493 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003494
Christopher Faulet61cc8522020-04-20 14:54:42 +02003495 /* we don't send any health-checks when the proxy is
3496 * stopped, the server should not be checked or the check
3497 * is disabled.
3498 */
3499 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3500 proxy->state == PR_STSTOPPED)
3501 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003502
Christopher Faulet61cc8522020-04-20 14:54:42 +02003503 /* we'll initiate a new check */
3504 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003505
Christopher Faulet61cc8522020-04-20 14:54:42 +02003506 check->state |= CHK_ST_INPROGRESS;
3507 b_reset(&check->bi);
3508 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003509
Christopher Faulet61cc8522020-04-20 14:54:42 +02003510 task_set_affinity(t, tid_bit);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003511
Christopher Faulet99ff1052020-05-25 07:32:01 +02003512 check->current_step = NULL;
3513 tcpcheck_main(check);
3514 goto out_unlock;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003515 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003516 else {
3517 /* there was a test running.
3518 * First, let's check whether there was an uncaught error,
3519 * which can happen on connect timeout or error.
3520 */
3521 if (check->result == CHK_RES_UNKNOWN) {
3522 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3523 chk_report_conn_err(check, 0, expired);
3524 }
3525 else
3526 goto out_unlock; /* timeout not reached, wait again */
3527 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003528
Christopher Faulet61cc8522020-04-20 14:54:42 +02003529 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003530
Christopher Faulet61cc8522020-04-20 14:54:42 +02003531 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003532
Christopher Faulet61cc8522020-04-20 14:54:42 +02003533 if (conn && conn->xprt) {
3534 /* The check was aborted and the connection was not yet closed.
3535 * This can happen upon timeout, or when an external event such
3536 * as a failed response coupled with "observe layer7" caused the
3537 * server state to be suddenly changed.
3538 */
3539 conn_sock_drain(conn);
3540 cs_close(cs);
3541 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003542
Christopher Faulet61cc8522020-04-20 14:54:42 +02003543 if (cs) {
3544 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003545 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003546 /* We may have been scheduled to run, and the
3547 * I/O handler expects to have a cs, so remove
3548 * the tasklet
3549 */
3550 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3551 cs_destroy(cs);
3552 cs = check->cs = NULL;
3553 conn = NULL;
3554 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003555
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003556 if (check->sess != NULL) {
3557 vars_prune(&check->vars, check->sess, NULL);
3558 session_free(check->sess);
3559 check->sess = NULL;
3560 }
3561
Christopher Faulet61cc8522020-04-20 14:54:42 +02003562 if (check->server) {
3563 if (check->result == CHK_RES_FAILED) {
3564 /* a failure or timeout detected */
3565 check_notify_failure(check);
3566 }
3567 else if (check->result == CHK_RES_CONDPASS) {
3568 /* check is OK but asks for stopping mode */
3569 check_notify_stopping(check);
3570 }
3571 else if (check->result == CHK_RES_PASSED) {
3572 /* a success was detected */
3573 check_notify_success(check);
3574 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003575 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003576 task_set_affinity(t, MAX_THREADS_MASK);
3577 check->state &= ~CHK_ST_INPROGRESS;
3578
3579 if (check->server) {
3580 rv = 0;
3581 if (global.spread_checks > 0) {
3582 rv = srv_getinter(check) * global.spread_checks / 100;
3583 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3584 }
3585 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003586 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003587 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003588
Christopher Faulet61cc8522020-04-20 14:54:42 +02003589 reschedule:
3590 while (tick_is_expired(t->expire, now_ms))
3591 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3592 out_unlock:
3593 if (check->server)
3594 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3595 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003596}
3597
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003598
Christopher Faulet61cc8522020-04-20 14:54:42 +02003599/**************************************************************************/
3600/******************* Internals to parse tcp-check rules *******************/
3601/**************************************************************************/
3602struct action_kw_list tcp_check_keywords = {
3603 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3604};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003605
Christopher Faulet61cc8522020-04-20 14:54:42 +02003606/* Return the struct action_kw associated to a keyword */
3607static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003608{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003609 return action_lookup(&tcp_check_keywords.list, kw);
3610}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003611
Christopher Faulet61cc8522020-04-20 14:54:42 +02003612static void action_kw_tcp_check_build_list(struct buffer *chk)
3613{
3614 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003615}
3616
Christopher Faulet61cc8522020-04-20 14:54:42 +02003617/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3618 * returned on error.
3619 */
3620static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3621 struct list *rules, struct action_kw *kw,
3622 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003623{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003624 struct tcpcheck_rule *chk = NULL;
3625 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003626
Christopher Faulet61cc8522020-04-20 14:54:42 +02003627 actrule = calloc(1, sizeof(*actrule));
3628 if (!actrule) {
3629 memprintf(errmsg, "out of memory");
3630 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003631 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003632 actrule->kw = kw;
3633 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003634
Christopher Faulet61cc8522020-04-20 14:54:42 +02003635 cur_arg++;
3636 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3637 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3638 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003639 }
3640
Christopher Faulet61cc8522020-04-20 14:54:42 +02003641 chk = calloc(1, sizeof(*chk));
3642 if (!chk) {
3643 memprintf(errmsg, "out of memory");
3644 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003645 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003646 chk->action = TCPCHK_ACT_ACTION_KW;
3647 chk->action_kw.rule = actrule;
3648 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003649
3650 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003651 free(actrule);
3652 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003653}
3654
Christopher Faulet61cc8522020-04-20 14:54:42 +02003655/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3656 * returned on error.
3657 */
3658static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3659 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003660{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003661 struct tcpcheck_rule *chk = NULL;
3662 struct sockaddr_storage *sk = NULL;
3663 char *comment = NULL, *sni = NULL, *alpn = NULL;
3664 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003665 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003666 unsigned short conn_opts = 0;
3667 long port = 0;
3668 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003669
Christopher Faulet61cc8522020-04-20 14:54:42 +02003670 list_for_each_entry(chk, rules, list) {
3671 if (chk->action == TCPCHK_ACT_CONNECT)
3672 break;
3673 if (chk->action == TCPCHK_ACT_COMMENT ||
3674 chk->action == TCPCHK_ACT_ACTION_KW ||
3675 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3676 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003677
Christopher Faulet61cc8522020-04-20 14:54:42 +02003678 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003679 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003680 "when there is a 'connect' step in the tcp-check ruleset");
3681 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003682 }
3683
Christopher Faulet61cc8522020-04-20 14:54:42 +02003684 cur_arg++;
3685 while (*(args[cur_arg])) {
3686 if (strcmp(args[cur_arg], "default") == 0)
3687 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3688 else if (strcmp(args[cur_arg], "addr") == 0) {
3689 int port1, port2;
3690 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003691
Christopher Faulet61cc8522020-04-20 14:54:42 +02003692 if (!*(args[cur_arg+1])) {
3693 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3694 goto error;
3695 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003696
Christopher Faulet61cc8522020-04-20 14:54:42 +02003697 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3698 if (!sk) {
3699 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3700 goto error;
3701 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003702
Christopher Faulet61cc8522020-04-20 14:54:42 +02003703 proto = protocol_by_family(sk->ss_family);
3704 if (!proto || !proto->connect) {
3705 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3706 args[cur_arg]);
3707 goto error;
3708 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003709
Christopher Faulet61cc8522020-04-20 14:54:42 +02003710 if (port1 != port2) {
3711 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3712 args[cur_arg], args[cur_arg+1]);
3713 goto error;
3714 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003715
Christopher Faulet61cc8522020-04-20 14:54:42 +02003716 cur_arg++;
3717 }
3718 else if (strcmp(args[cur_arg], "port") == 0) {
3719 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003720
Christopher Faulet61cc8522020-04-20 14:54:42 +02003721 if (!*(args[cur_arg+1])) {
3722 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3723 goto error;
3724 }
3725 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003726
Christopher Faulet61cc8522020-04-20 14:54:42 +02003727 port = 0;
3728 release_sample_expr(port_expr);
3729 p = args[cur_arg]; end = p + strlen(p);
3730 port = read_uint(&p, end);
3731 if (p != end) {
3732 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003733
Christopher Faulet61cc8522020-04-20 14:54:42 +02003734 px->conf.args.ctx = ARGC_SRV;
3735 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3736 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003737
Christopher Faulet61cc8522020-04-20 14:54:42 +02003738 if (!port_expr) {
3739 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3740 goto error;
3741 }
3742 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3743 memprintf(errmsg, "error detected while parsing port expression : "
3744 " fetch method '%s' extracts information from '%s', "
3745 "none of which is available here.\n",
3746 args[cur_arg], sample_src_names(port_expr->fetch->use));
3747 goto error;
3748 }
3749 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3750 }
3751 else if (port > 65535 || port < 1) {
3752 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3753 args[cur_arg]);
3754 goto error;
3755 }
3756 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003757 else if (strcmp(args[cur_arg], "proto") == 0) {
3758 if (!*(args[cur_arg+1])) {
3759 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3760 goto error;
3761 }
3762 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3763 if (!mux_proto) {
3764 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3765 goto error;
3766 }
3767 cur_arg++;
3768 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003769 else if (strcmp(args[cur_arg], "comment") == 0) {
3770 if (!*(args[cur_arg+1])) {
3771 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3772 goto error;
3773 }
3774 cur_arg++;
3775 free(comment);
3776 comment = strdup(args[cur_arg]);
3777 if (!comment) {
3778 memprintf(errmsg, "out of memory");
3779 goto error;
3780 }
3781 }
3782 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3783 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3784 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3785 conn_opts |= TCPCHK_OPT_SOCKS4;
3786 else if (strcmp(args[cur_arg], "linger") == 0)
3787 conn_opts |= TCPCHK_OPT_LINGER;
3788#ifdef USE_OPENSSL
3789 else if (strcmp(args[cur_arg], "ssl") == 0) {
3790 px->options |= PR_O_TCPCHK_SSL;
3791 conn_opts |= TCPCHK_OPT_SSL;
3792 }
3793 else if (strcmp(args[cur_arg], "sni") == 0) {
3794 if (!*(args[cur_arg+1])) {
3795 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3796 goto error;
3797 }
3798 cur_arg++;
3799 free(sni);
3800 sni = strdup(args[cur_arg]);
3801 if (!sni) {
3802 memprintf(errmsg, "out of memory");
3803 goto error;
3804 }
3805 }
3806 else if (strcmp(args[cur_arg], "alpn") == 0) {
3807#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3808 free(alpn);
3809 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3810 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3811 goto error;
3812 }
3813 cur_arg++;
3814#else
3815 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003816 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003817#endif
3818 }
3819#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003820
Christopher Faulet61cc8522020-04-20 14:54:42 +02003821 else {
3822 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3823#ifdef USE_OPENSSL
3824 ", 'ssl', 'sni', 'alpn'"
3825#endif /* USE_OPENSSL */
3826 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3827 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003828 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003829 }
3830 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003831 }
3832
Christopher Faulet61cc8522020-04-20 14:54:42 +02003833 chk = calloc(1, sizeof(*chk));
3834 if (!chk) {
3835 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003836 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003837 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003838 chk->action = TCPCHK_ACT_CONNECT;
3839 chk->comment = comment;
3840 chk->connect.port = port;
3841 chk->connect.options = conn_opts;
3842 chk->connect.sni = sni;
3843 chk->connect.alpn = alpn;
3844 chk->connect.alpn_len= alpn_len;
3845 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003846 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003847 if (sk)
3848 chk->connect.addr = *sk;
3849 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003850
Christopher Faulet61cc8522020-04-20 14:54:42 +02003851 error:
3852 free(alpn);
3853 free(sni);
3854 free(comment);
3855 release_sample_expr(port_expr);
3856 return NULL;
3857}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003858
Christopher Faulet61cc8522020-04-20 14:54:42 +02003859/* Parses and creates a tcp-check send rule. NULL is returned on error */
3860static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3861 const char *file, int line, char **errmsg)
3862{
3863 struct tcpcheck_rule *chk = NULL;
3864 char *comment = NULL, *data = NULL;
3865 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003866
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003867 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3868 type = TCPCHK_SEND_BINARY_LF;
3869 else if (strcmp(args[cur_arg], "send-binary") == 0)
3870 type = TCPCHK_SEND_BINARY;
3871 else if (strcmp(args[cur_arg], "send-lf") == 0)
3872 type = TCPCHK_SEND_STRING_LF;
3873 else if (strcmp(args[cur_arg], "send") == 0)
3874 type = TCPCHK_SEND_STRING;
3875
Christopher Faulet61cc8522020-04-20 14:54:42 +02003876 if (!*(args[cur_arg+1])) {
3877 memprintf(errmsg, "'%s' expects a %s as argument",
3878 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003879 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003880 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003881
Christopher Faulet61cc8522020-04-20 14:54:42 +02003882 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003883
Christopher Faulet61cc8522020-04-20 14:54:42 +02003884 cur_arg += 2;
3885 while (*(args[cur_arg])) {
3886 if (strcmp(args[cur_arg], "comment") == 0) {
3887 if (!*(args[cur_arg+1])) {
3888 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3889 goto error;
3890 }
3891 cur_arg++;
3892 free(comment);
3893 comment = strdup(args[cur_arg]);
3894 if (!comment) {
3895 memprintf(errmsg, "out of memory");
3896 goto error;
3897 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003898 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003899 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003900 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003901 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003902 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003903 }
3904 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003905 }
3906
Christopher Faulet61cc8522020-04-20 14:54:42 +02003907 chk = calloc(1, sizeof(*chk));
3908 if (!chk) {
3909 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003910 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003911 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003912 chk->action = TCPCHK_ACT_SEND;
3913 chk->comment = comment;
3914 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003915
Christopher Faulet61cc8522020-04-20 14:54:42 +02003916 switch (chk->send.type) {
3917 case TCPCHK_SEND_STRING:
3918 chk->send.data = ist2(strdup(data), strlen(data));
3919 if (!isttest(chk->send.data)) {
3920 memprintf(errmsg, "out of memory");
3921 goto error;
3922 }
3923 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003924 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003925 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003926 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003927 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3928 goto error;
3929 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003930 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003931 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003932 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003933 case TCPCHK_SEND_STRING_LF:
3934 case TCPCHK_SEND_BINARY_LF:
3935 LIST_INIT(&chk->send.fmt);
3936 px->conf.args.ctx = ARGC_SRV;
3937 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3938 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3939 goto error;
3940 }
3941 break;
3942 case TCPCHK_SEND_HTTP:
3943 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003944 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003945 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003946
Christopher Faulet61cc8522020-04-20 14:54:42 +02003947 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003948
Christopher Faulet61cc8522020-04-20 14:54:42 +02003949 error:
3950 free(chk);
3951 free(comment);
3952 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003953}
3954
Christopher Faulet61cc8522020-04-20 14:54:42 +02003955/* Parses and creates a http-check send rule. NULL is returned on error */
3956static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3957 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003958{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003959 struct tcpcheck_rule *chk = NULL;
3960 struct tcpcheck_http_hdr *hdr = NULL;
3961 struct http_hdr hdrs[global.tune.max_http_hdr];
3962 char *meth = NULL, *uri = NULL, *vsn = NULL;
3963 char *body = NULL, *comment = NULL;
3964 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003965 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003966
3967 cur_arg++;
3968 while (*(args[cur_arg])) {
3969 if (strcmp(args[cur_arg], "meth") == 0) {
3970 if (!*(args[cur_arg+1])) {
3971 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3972 goto error;
3973 }
3974 cur_arg++;
3975 meth = args[cur_arg];
3976 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003977 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003978 if (!*(args[cur_arg+1])) {
3979 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3980 goto error;
3981 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003982 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
3983 if (strcmp(args[cur_arg], "uri-lf") == 0)
3984 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003985 cur_arg++;
3986 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02003987 }
Christopher Faulet907701b2020-04-28 09:37:00 +02003988 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003989 if (!*(args[cur_arg+1])) {
3990 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3991 goto error;
3992 }
3993 cur_arg++;
3994 vsn = args[cur_arg];
3995 }
3996 else if (strcmp(args[cur_arg], "hdr") == 0) {
3997 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
3998 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
3999 goto error;
4000 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004001
4002 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4003 if (host_hdr >= 0) {
4004 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4005 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4006 goto error;
4007 }
4008 host_hdr = i;
4009 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004010 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4011 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4012 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4013 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004014
Christopher Faulet61cc8522020-04-20 14:54:42 +02004015 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4016 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4017 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004018 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004019 cur_arg += 2;
4020 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004021 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004022 if (!*(args[cur_arg+1])) {
4023 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4024 goto error;
4025 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004026 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4027 if (strcmp(args[cur_arg], "body-lf") == 0)
4028 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004029 cur_arg++;
4030 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004031 }
4032 else if (strcmp(args[cur_arg], "comment") == 0) {
4033 if (!*(args[cur_arg+1])) {
4034 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4035 goto error;
4036 }
4037 cur_arg++;
4038 free(comment);
4039 comment = strdup(args[cur_arg]);
4040 if (!comment) {
4041 memprintf(errmsg, "out of memory");
4042 goto error;
4043 }
4044 }
4045 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004046 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004047 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004048 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004049 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004050 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004051 }
4052
Christopher Faulet61cc8522020-04-20 14:54:42 +02004053 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004054
Christopher Faulet61cc8522020-04-20 14:54:42 +02004055 chk = calloc(1, sizeof(*chk));
4056 if (!chk) {
4057 memprintf(errmsg, "out of memory");
4058 goto error;
4059 }
4060 chk->action = TCPCHK_ACT_SEND;
4061 chk->comment = comment; comment = NULL;
4062 chk->send.type = TCPCHK_SEND_HTTP;
4063 chk->send.http.flags = flags;
4064 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004065
Christopher Faulet61cc8522020-04-20 14:54:42 +02004066 if (meth) {
4067 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4068 chk->send.http.meth.str.area = strdup(meth);
4069 chk->send.http.meth.str.data = strlen(meth);
4070 if (!chk->send.http.meth.str.area) {
4071 memprintf(errmsg, "out of memory");
4072 goto error;
4073 }
4074 }
4075 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004076 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4077 LIST_INIT(&chk->send.http.uri_fmt);
4078 px->conf.args.ctx = ARGC_SRV;
4079 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4080 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4081 goto error;
4082 }
4083 }
4084 else {
4085 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4086 if (!isttest(chk->send.http.uri)) {
4087 memprintf(errmsg, "out of memory");
4088 goto error;
4089 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004090 }
4091 }
4092 if (vsn) {
4093 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4094 if (!isttest(chk->send.http.vsn)) {
4095 memprintf(errmsg, "out of memory");
4096 goto error;
4097 }
4098 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004099 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004100 hdr = calloc(1, sizeof(*hdr));
4101 if (!hdr) {
4102 memprintf(errmsg, "out of memory");
4103 goto error;
4104 }
4105 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004106 hdr->name = istdup(hdrs[i].n);
4107 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004108 memprintf(errmsg, "out of memory");
4109 goto error;
4110 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004111
Christopher Fauletb61caf42020-04-21 10:57:42 +02004112 ist0(hdrs[i].v);
4113 if (!parse_logformat_string(istptr(hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
Christopher Faulet61cc8522020-04-20 14:54:42 +02004114 goto error;
4115 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4116 hdr = NULL;
4117 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004118
Christopher Faulet61cc8522020-04-20 14:54:42 +02004119 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004120 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4121 LIST_INIT(&chk->send.http.body_fmt);
4122 px->conf.args.ctx = ARGC_SRV;
4123 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4124 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4125 goto error;
4126 }
4127 }
4128 else {
4129 chk->send.http.body = ist2(strdup(body), strlen(body));
4130 if (!isttest(chk->send.http.body)) {
4131 memprintf(errmsg, "out of memory");
4132 goto error;
4133 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004134 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004135 }
4136
Christopher Faulet61cc8522020-04-20 14:54:42 +02004137 return chk;
4138
4139 error:
4140 free_tcpcheck_http_hdr(hdr);
4141 free_tcpcheck(chk, 0);
4142 free(comment);
4143 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004144}
4145
Christopher Faulet61cc8522020-04-20 14:54:42 +02004146/* Parses and creates a http-check comment rule. NULL is returned on error */
4147static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4148 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004149{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004150 struct tcpcheck_rule *chk = NULL;
4151 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004152
Christopher Faulet61cc8522020-04-20 14:54:42 +02004153 if (!*(args[cur_arg+1])) {
4154 memprintf(errmsg, "expects a string as argument");
4155 goto error;
4156 }
4157 cur_arg++;
4158 comment = strdup(args[cur_arg]);
4159 if (!comment) {
4160 memprintf(errmsg, "out of memory");
4161 goto error;
4162 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004163
Christopher Faulet61cc8522020-04-20 14:54:42 +02004164 chk = calloc(1, sizeof(*chk));
4165 if (!chk) {
4166 memprintf(errmsg, "out of memory");
4167 goto error;
4168 }
4169 chk->action = TCPCHK_ACT_COMMENT;
4170 chk->comment = comment;
4171 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004172
Christopher Faulet61cc8522020-04-20 14:54:42 +02004173 error:
4174 free(comment);
4175 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004176}
4177
Christopher Faulet61cc8522020-04-20 14:54:42 +02004178/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4179 * on error. <proto> is set to the right protocol flags (covered by the
4180 * TCPCHK_RULES_PROTO_CHK mask).
4181 */
4182static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4183 struct list *rules, unsigned int proto,
4184 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004185{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004186 struct tcpcheck_rule *prev_check, *chk = NULL;
4187 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004188 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004189 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004190 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4191 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4192 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004193 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004194 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004195 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004196
Christopher Faulet39708192020-05-05 10:47:36 +02004197 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004198 if (!*(args[cur_arg+1])) {
4199 memprintf(errmsg, "expects at least a matching pattern as arguments");
4200 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004201 }
4202
Christopher Faulet61cc8522020-04-20 14:54:42 +02004203 cur_arg++;
4204 while (*(args[cur_arg])) {
4205 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004206
Christopher Faulet61cc8522020-04-20 14:54:42 +02004207 rescan:
4208 if (strcmp(args[cur_arg], "min-recv") == 0) {
4209 if (in_pattern) {
4210 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4211 goto error;
4212 }
4213 if (!*(args[cur_arg+1])) {
4214 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4215 goto error;
4216 }
4217 /* Use an signed integer here because of chksize */
4218 cur_arg++;
4219 min_recv = atol(args[cur_arg]);
4220 if (min_recv < -1 || min_recv > INT_MAX) {
4221 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4222 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004223 }
4224 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004225 else if (*(args[cur_arg]) == '!') {
4226 in_pattern = 1;
4227 while (*(args[cur_arg]) == '!') {
4228 inverse = !inverse;
4229 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004230 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004231 if (!*(args[cur_arg]))
4232 cur_arg++;
4233 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004234 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004235 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4236 if (type != TCPCHK_EXPECT_UNDEF) {
4237 memprintf(errmsg, "only on pattern expected");
4238 goto error;
4239 }
4240 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004241 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004242 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004243 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004244
Christopher Faulet61cc8522020-04-20 14:54:42 +02004245 if (!*(args[cur_arg+1])) {
4246 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4247 goto error;
4248 }
4249 cur_arg++;
4250 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004251 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004252 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4253 if (proto == TCPCHK_RULES_HTTP_CHK)
4254 goto bad_http_kw;
4255 if (type != TCPCHK_EXPECT_UNDEF) {
4256 memprintf(errmsg, "only on pattern expected");
4257 goto error;
4258 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004259 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004260
Christopher Faulet61cc8522020-04-20 14:54:42 +02004261 if (!*(args[cur_arg+1])) {
4262 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4263 goto error;
4264 }
4265 cur_arg++;
4266 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004267 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004268 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4269 if (type != TCPCHK_EXPECT_UNDEF) {
4270 memprintf(errmsg, "only on pattern expected");
4271 goto error;
4272 }
4273 if (proto != TCPCHK_RULES_HTTP_CHK)
4274 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4275 else {
4276 if (*(args[cur_arg]) != 's')
4277 goto bad_http_kw;
4278 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4279 }
4280
4281 if (!*(args[cur_arg+1])) {
4282 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4283 goto error;
4284 }
4285 cur_arg++;
4286 pattern = args[cur_arg];
4287 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004288 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4289 if (proto != TCPCHK_RULES_HTTP_CHK)
4290 goto bad_tcp_kw;
4291 if (type != TCPCHK_EXPECT_UNDEF) {
4292 memprintf(errmsg, "only on pattern expected");
4293 goto error;
4294 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004295 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004296
Christopher Faulet61cc8522020-04-20 14:54:42 +02004297 if (!*(args[cur_arg+1])) {
4298 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4299 goto error;
4300 }
4301 cur_arg++;
4302 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004303 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004304 else if (strcmp(args[cur_arg], "custom") == 0) {
4305 if (in_pattern) {
4306 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4307 goto error;
4308 }
4309 if (type != TCPCHK_EXPECT_UNDEF) {
4310 memprintf(errmsg, "only on pattern expected");
4311 goto error;
4312 }
4313 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004314 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004315 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004316 int orig_arg = cur_arg;
4317
4318 if (proto != TCPCHK_RULES_HTTP_CHK)
4319 goto bad_tcp_kw;
4320 if (type != TCPCHK_EXPECT_UNDEF) {
4321 memprintf(errmsg, "only on pattern expected");
4322 goto error;
4323 }
4324 type = TCPCHK_EXPECT_HTTP_HEADER;
4325
Christopher Fauletb5594262020-05-05 20:23:13 +02004326 if (strcmp(args[cur_arg], "fhdr") == 0)
4327 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4328
Christopher Faulet39708192020-05-05 10:47:36 +02004329 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004330 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4331 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4332 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004333 args[orig_arg]);
4334 goto error;
4335 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004336
4337 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4338 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4339
Christopher Faulet39708192020-05-05 10:47:36 +02004340 cur_arg += 2;
4341 if (strcmp(args[cur_arg], "-m") == 0) {
4342 if (!*(args[cur_arg+1])) {
4343 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4344 args[orig_arg], args[cur_arg]);
4345 goto error;
4346 }
4347 if (strcmp(args[cur_arg+1], "str") == 0)
4348 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4349 else if (strcmp(args[cur_arg+1], "beg") == 0)
4350 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4351 else if (strcmp(args[cur_arg+1], "end") == 0)
4352 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4353 else if (strcmp(args[cur_arg+1], "sub") == 0)
4354 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004355 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4356 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4357 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4358 args[orig_arg]);
4359 goto error;
4360 }
Christopher Faulet39708192020-05-05 10:47:36 +02004361 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004362 }
Christopher Faulet39708192020-05-05 10:47:36 +02004363 else {
4364 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4365 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4366 goto error;
4367 }
4368 cur_arg += 2;
4369 }
4370 else
4371 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4372 npat = args[cur_arg];
4373
Christopher Fauletb5594262020-05-05 20:23:13 +02004374 if (!*(args[cur_arg+1]) ||
4375 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004376 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4377 goto next;
4378 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004379 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4380 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004381
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004382 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004383 if (strcmp(args[cur_arg+2], "-m") == 0) {
4384 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004385 if (!*(args[cur_arg+1])) {
4386 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4387 args[orig_arg], args[cur_arg]);
4388 goto error;
4389 }
4390 if (strcmp(args[cur_arg+1], "str") == 0)
4391 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4392 else if (strcmp(args[cur_arg+1], "beg") == 0)
4393 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4394 else if (strcmp(args[cur_arg+1], "end") == 0)
4395 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4396 else if (strcmp(args[cur_arg+1], "sub") == 0)
4397 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004398 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4399 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4400 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4401 args[orig_arg]);
4402 goto error;
4403 }
Christopher Faulet39708192020-05-05 10:47:36 +02004404 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004405 }
Christopher Faulet39708192020-05-05 10:47:36 +02004406 else {
4407 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4408 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4409 goto error;
4410 }
Christopher Faulet39708192020-05-05 10:47:36 +02004411 }
4412 else
4413 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004414
Christopher Fauletb5594262020-05-05 20:23:13 +02004415 if (!*(args[cur_arg+2])) {
4416 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4417 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004418 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004419 vpat = args[cur_arg+2];
4420 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004421 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004422 else if (strcmp(args[cur_arg], "comment") == 0) {
4423 if (in_pattern) {
4424 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4425 goto error;
4426 }
4427 if (!*(args[cur_arg+1])) {
4428 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4429 goto error;
4430 }
4431 cur_arg++;
4432 free(comment);
4433 comment = strdup(args[cur_arg]);
4434 if (!comment) {
4435 memprintf(errmsg, "out of memory");
4436 goto error;
4437 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004438 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004439 else if (strcmp(args[cur_arg], "on-success") == 0) {
4440 if (in_pattern) {
4441 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4442 goto error;
4443 }
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++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004449 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004450 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004451 else if (strcmp(args[cur_arg], "on-error") == 0) {
4452 if (in_pattern) {
4453 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4454 goto error;
4455 }
4456 if (!*(args[cur_arg+1])) {
4457 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4458 goto error;
4459 }
4460 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004461 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004462 }
4463 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4464 if (in_pattern) {
4465 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4466 goto error;
4467 }
4468 if (!*(args[cur_arg+1])) {
4469 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4470 goto error;
4471 }
4472 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4473 ok_st = HCHK_STATUS_L7OKD;
4474 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4475 ok_st = HCHK_STATUS_L7OKCD;
4476 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4477 ok_st = HCHK_STATUS_L6OK;
4478 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4479 ok_st = HCHK_STATUS_L4OK;
4480 else {
4481 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4482 args[cur_arg], args[cur_arg+1]);
4483 goto error;
4484 }
4485 cur_arg++;
4486 }
4487 else if (strcmp(args[cur_arg], "error-status") == 0) {
4488 if (in_pattern) {
4489 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4490 goto error;
4491 }
4492 if (!*(args[cur_arg+1])) {
4493 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4494 goto error;
4495 }
4496 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4497 err_st = HCHK_STATUS_L7RSP;
4498 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4499 err_st = HCHK_STATUS_L7STS;
4500 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4501 err_st = HCHK_STATUS_L6RSP;
4502 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4503 err_st = HCHK_STATUS_L4CON;
4504 else {
4505 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4506 args[cur_arg], args[cur_arg+1]);
4507 goto error;
4508 }
4509 cur_arg++;
4510 }
4511 else if (strcmp(args[cur_arg], "status-code") == 0) {
4512 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004513
Christopher Faulet61cc8522020-04-20 14:54:42 +02004514 if (in_pattern) {
4515 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4516 goto error;
4517 }
4518 if (!*(args[cur_arg+1])) {
4519 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4520 goto error;
4521 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004522
Christopher Faulet61cc8522020-04-20 14:54:42 +02004523 cur_arg++;
4524 release_sample_expr(status_expr);
4525 px->conf.args.ctx = ARGC_SRV;
4526 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4527 file, line, errmsg, &px->conf.args, NULL);
4528 if (!status_expr) {
4529 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4530 goto error;
4531 }
4532 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4533 memprintf(errmsg, "error detected while parsing status-code expression : "
4534 " fetch method '%s' extracts information from '%s', "
4535 "none of which is available here.\n",
4536 args[cur_arg], sample_src_names(status_expr->fetch->use));
4537 goto error;
4538 }
4539 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4540 }
4541 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4542 if (in_pattern) {
4543 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4544 goto error;
4545 }
4546 if (!*(args[cur_arg+1])) {
4547 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4548 goto error;
4549 }
4550 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4551 tout_st = HCHK_STATUS_L7TOUT;
4552 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4553 tout_st = HCHK_STATUS_L6TOUT;
4554 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4555 tout_st = HCHK_STATUS_L4TOUT;
4556 else {
4557 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4558 args[cur_arg], args[cur_arg+1]);
4559 goto error;
4560 }
4561 cur_arg++;
4562 }
4563 else {
4564 if (proto == TCPCHK_RULES_HTTP_CHK) {
4565 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004566 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004567 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004568 }
4569 else {
4570 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004571 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4572 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004573 }
4574 goto error;
4575 }
Christopher Faulet39708192020-05-05 10:47:36 +02004576 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004577 cur_arg++;
4578 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004579
Christopher Faulet61cc8522020-04-20 14:54:42 +02004580 chk = calloc(1, sizeof(*chk));
4581 if (!chk) {
4582 memprintf(errmsg, "out of memory");
4583 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004584 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004585 chk->action = TCPCHK_ACT_EXPECT;
4586 LIST_INIT(&chk->expect.onerror_fmt);
4587 LIST_INIT(&chk->expect.onsuccess_fmt);
4588 chk->comment = comment; comment = NULL;
4589 chk->expect.type = type;
4590 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004591 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004592 chk->expect.ok_status = ok_st;
4593 chk->expect.err_status = err_st;
4594 chk->expect.tout_status = tout_st;
4595 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004596
Christopher Faulet61cc8522020-04-20 14:54:42 +02004597 if (on_success_msg) {
4598 px->conf.args.ctx = ARGC_SRV;
4599 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4600 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4601 goto error;
4602 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004603 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004604 if (on_error_msg) {
4605 px->conf.args.ctx = ARGC_SRV;
4606 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4607 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4608 goto error;
4609 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004610 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004611
Christopher Faulet61cc8522020-04-20 14:54:42 +02004612 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004613 case TCPCHK_EXPECT_HTTP_STATUS: {
4614 const char *p = pattern;
4615 unsigned int c1,c2;
4616
4617 chk->expect.codes.codes = NULL;
4618 chk->expect.codes.num = 0;
4619 while (1) {
4620 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4621 if (*p == '-') {
4622 p++;
4623 c2 = read_uint(&p, pattern + strlen(pattern));
4624 }
4625 if (c1 > c2) {
4626 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4627 goto error;
4628 }
4629
4630 chk->expect.codes.num++;
4631 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4632 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4633 if (!chk->expect.codes.codes) {
4634 memprintf(errmsg, "out of memory");
4635 goto error;
4636 }
4637 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4638 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4639
4640 if (*p == '\0')
4641 break;
4642 if (*p != ',') {
4643 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4644 goto error;
4645 }
4646 p++;
4647 }
4648 break;
4649 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004650 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004651 case TCPCHK_EXPECT_HTTP_BODY:
4652 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004653 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004654 memprintf(errmsg, "out of memory");
4655 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004656 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004657 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004658 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004659 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004660
4661 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004662 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4663 goto error;
4664 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004665 chk->expect.data.len = len;
4666 break;
4667 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004668 case TCPCHK_EXPECT_STRING_REGEX:
4669 case TCPCHK_EXPECT_BINARY_REGEX:
4670 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4671 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004672 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004673 if (!chk->expect.regex)
4674 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004675 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004676
4677 case TCPCHK_EXPECT_STRING_LF:
4678 case TCPCHK_EXPECT_BINARY_LF:
4679 case TCPCHK_EXPECT_HTTP_BODY_LF:
4680 LIST_INIT(&chk->expect.fmt);
4681 px->conf.args.ctx = ARGC_SRV;
4682 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4683 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4684 goto error;
4685 }
4686 break;
4687
Christopher Faulet39708192020-05-05 10:47:36 +02004688 case TCPCHK_EXPECT_HTTP_HEADER:
4689 if (!npat) {
4690 memprintf(errmsg, "unexpected error, undefined header name pattern");
4691 goto error;
4692 }
4693 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4694 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4695 if (!chk->expect.hdr.name_re)
4696 goto error;
4697 }
4698 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4699 px->conf.args.ctx = ARGC_SRV;
4700 LIST_INIT(&chk->expect.hdr.name_fmt);
4701 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4702 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4703 goto error;
4704 }
4705 }
4706 else {
4707 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4708 if (!isttest(chk->expect.hdr.name)) {
4709 memprintf(errmsg, "out of memory");
4710 goto error;
4711 }
4712 }
4713
4714 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4715 chk->expect.hdr.value = IST_NULL;
4716 break;
4717 }
4718
4719 if (!vpat) {
4720 memprintf(errmsg, "unexpected error, undefined header value pattern");
4721 goto error;
4722 }
4723 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4724 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4725 if (!chk->expect.hdr.value_re)
4726 goto error;
4727 }
4728 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4729 px->conf.args.ctx = ARGC_SRV;
4730 LIST_INIT(&chk->expect.hdr.value_fmt);
4731 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4732 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4733 goto error;
4734 }
4735 }
4736 else {
4737 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4738 if (!isttest(chk->expect.hdr.value)) {
4739 memprintf(errmsg, "out of memory");
4740 goto error;
4741 }
4742 }
4743
Christopher Faulet61cc8522020-04-20 14:54:42 +02004744 break;
4745 case TCPCHK_EXPECT_CUSTOM:
4746 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4747 break;
4748 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004749 memprintf(errmsg, "pattern not found");
4750 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004751 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004752
Christopher Faulet61cc8522020-04-20 14:54:42 +02004753 /* All tcp-check expect points back to the first inverse expect rule in
4754 * a chain of one or more expect rule, potentially itself.
4755 */
4756 chk->expect.head = chk;
4757 list_for_each_entry_rev(prev_check, rules, list) {
4758 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4759 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4760 chk->expect.head = prev_check;
4761 continue;
4762 }
4763 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4764 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004765 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004766 return chk;
4767
4768 error:
4769 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004770 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004771 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004772 return NULL;
4773}
4774
Christopher Faulet61cc8522020-04-20 14:54:42 +02004775/* Overwrites fields of the old http send rule with those of the new one. When
4776 * replaced, old values are freed and replaced by the new ones. New values are
4777 * not copied but transferred. At the end <new> should be empty and can be
4778 * safely released. This function never fails.
4779 */
4780static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004781{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004782 struct logformat_node *lf, *lfb;
4783 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004784
Christopher Faulet404f9192020-04-09 23:13:54 +02004785
Christopher Faulet61cc8522020-04-20 14:54:42 +02004786 if (new->send.http.meth.str.area) {
4787 free(old->send.http.meth.str.area);
4788 old->send.http.meth.meth = new->send.http.meth.meth;
4789 old->send.http.meth.str.area = new->send.http.meth.str.area;
4790 old->send.http.meth.str.data = new->send.http.meth.str.data;
4791 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004792 }
4793
Christopher Faulet61cc8522020-04-20 14:54:42 +02004794 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4795 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004796 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004797 else
4798 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4799 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4800 old->send.http.uri = new->send.http.uri;
4801 new->send.http.uri = IST_NULL;
4802 }
4803 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4804 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004805 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004806 else
4807 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4808 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4809 LIST_INIT(&old->send.http.uri_fmt);
4810 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4811 LIST_DEL(&lf->list);
4812 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4813 }
4814 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004815
Christopher Faulet61cc8522020-04-20 14:54:42 +02004816 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004817 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004818 old->send.http.vsn = new->send.http.vsn;
4819 new->send.http.vsn = IST_NULL;
4820 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004821
Christopher Faulet61cc8522020-04-20 14:54:42 +02004822 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4823 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4824 LIST_DEL(&hdr->list);
4825 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004826 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004827
4828 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4829 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004830 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004831 else
4832 free_tcpcheck_fmt(&old->send.http.body_fmt);
4833 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4834 old->send.http.body = new->send.http.body;
4835 new->send.http.body = IST_NULL;
4836 }
4837 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4838 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004839 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004840 else
4841 free_tcpcheck_fmt(&old->send.http.body_fmt);
4842 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4843 LIST_INIT(&old->send.http.body_fmt);
4844 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4845 LIST_DEL(&lf->list);
4846 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4847 }
4848 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004849}
4850
Christopher Faulet61cc8522020-04-20 14:54:42 +02004851/* Internal function used to add an http-check rule in a list during the config
4852 * parsing step. Depending on its type, and the previously inserted rules, a
4853 * specific action may be performed or an error may be reported. This functions
4854 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4855 * message.
4856 */
4857static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004858{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004859 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004860
Christopher Faulet61cc8522020-04-20 14:54:42 +02004861 /* the implicit send rule coming from an "option httpchk" line must be
4862 * merged with the first explici http-check send rule, if
4863 * any. Depdending the declaration order some tests are required.
4864 *
4865 * Some tests is also required for other kinds of http-check rules to be
4866 * sure the ruleset remains valid.
4867 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004868
Christopher Faulet61cc8522020-04-20 14:54:42 +02004869 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004870 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004871 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4872 * following tests are performed :
4873 *
4874 * 1- If there is no such rule or if it is not a send rule, the implicit send
4875 * rule is pushed in front of the ruleset
4876 *
4877 * 2- If it is another implicit send rule, it is replaced with the new one.
4878 *
4879 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004880 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004881 * new send rule (the implicit one).
4882 */
4883 r = get_first_tcpcheck_rule(rules);
4884 if (r && r->action == TCPCHK_ACT_CONNECT)
4885 r = get_next_tcpcheck_rule(rules, r);
4886 if (!r || r->action != TCPCHK_ACT_SEND)
4887 LIST_ADD(rules->list, &chk->list);
4888 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4889 LIST_DEL(&r->list);
4890 free_tcpcheck(r, 0);
4891 LIST_ADD(rules->list, &chk->list);
4892 }
4893 else {
4894 tcpcheck_overwrite_send_http_rule(r, chk);
4895 free_tcpcheck(chk, 0);
4896 }
4897 }
4898 else {
4899 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4900 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4901 * with an existing implicit send rule, if any. At the end, if there is no error,
4902 * the rule is appended to the list.
4903 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004904
Christopher Faulet61cc8522020-04-20 14:54:42 +02004905 r = get_last_tcpcheck_rule(rules);
4906 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4907 /* no error */;
4908 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4909 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4910 chk->index+1);
4911 return 0;
4912 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004913 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004914 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4915 chk->index+1);
4916 return 0;
4917 }
4918 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4919 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4920 chk->index+1);
4921 return 0;
4922 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004923
Christopher Faulet61cc8522020-04-20 14:54:42 +02004924 if (chk->action == TCPCHK_ACT_SEND) {
4925 r = get_first_tcpcheck_rule(rules);
4926 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4927 tcpcheck_overwrite_send_http_rule(r, chk);
4928 free_tcpcheck(chk, 0);
4929 LIST_DEL(&r->list);
4930 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4931 chk = r;
4932 }
4933 }
4934 LIST_ADDQ(rules->list, &chk->list);
4935 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004936 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004937}
4938
Christopher Faulet61cc8522020-04-20 14:54:42 +02004939/**************************************************************************/
4940/************************** Init/deinit checks ****************************/
4941/**************************************************************************/
4942static const char *init_check(struct check *check, int type)
4943{
4944 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004945
Christopher Faulet61cc8522020-04-20 14:54:42 +02004946 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4947 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004948
Christopher Faulet61cc8522020-04-20 14:54:42 +02004949 check->bi.area = calloc(check->bi.size, sizeof(char));
4950 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004951
Christopher Faulet61cc8522020-04-20 14:54:42 +02004952 if (!check->bi.area || !check->bo.area)
4953 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004954
Christopher Faulet61cc8522020-04-20 14:54:42 +02004955 check->wait_list.tasklet = tasklet_new();
4956 if (!check->wait_list.tasklet)
4957 return "out of memory while allocating check tasklet";
4958 check->wait_list.events = 0;
4959 check->wait_list.tasklet->process = event_srv_chk_io;
4960 check->wait_list.tasklet->context = check;
4961 return NULL;
4962}
4963
4964void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004965{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004966 task_destroy(check->task);
4967 if (check->wait_list.tasklet)
4968 tasklet_free(check->wait_list.tasklet);
4969
4970 free(check->bi.area);
4971 free(check->bo.area);
4972 if (check->cs) {
4973 free(check->cs->conn);
4974 check->cs->conn = NULL;
4975 cs_free(check->cs);
4976 check->cs = NULL;
4977 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004978}
4979
Christopher Faulet61cc8522020-04-20 14:54:42 +02004980/* manages a server health-check. Returns the time the task accepts to wait, or
4981 * TIME_ETERNITY for infinity.
4982 */
4983static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004984{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004985 struct check *check = context;
4986
4987 if (check->type == PR_O2_EXT_CHK)
4988 return process_chk_proc(t, context, state);
4989 return process_chk_conn(t, context, state);
4990
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004991}
4992
Christopher Faulet61cc8522020-04-20 14:54:42 +02004993
4994static int start_check_task(struct check *check, int mininter,
4995 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004996{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004997 struct task *t;
4998 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004999
Christopher Faulet61cc8522020-04-20 14:54:42 +02005000 if (check->type == PR_O2_EXT_CHK)
5001 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005002
Christopher Faulet61cc8522020-04-20 14:54:42 +02005003 /* task for the check */
5004 if ((t = task_new(thread_mask)) == NULL) {
5005 ha_alert("Starting [%s:%s] check: out of memory.\n",
5006 check->server->proxy->id, check->server->id);
5007 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005008 }
5009
Christopher Faulet61cc8522020-04-20 14:54:42 +02005010 check->task = t;
5011 t->process = process_chk;
5012 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005013
Christopher Faulet61cc8522020-04-20 14:54:42 +02005014 if (mininter < srv_getinter(check))
5015 mininter = srv_getinter(check);
5016
5017 if (global.max_spread_checks && mininter > global.max_spread_checks)
5018 mininter = global.max_spread_checks;
5019
5020 /* check this every ms */
5021 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5022 check->start = now;
5023 task_queue(t);
5024
5025 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005026}
5027
Christopher Faulet61cc8522020-04-20 14:54:42 +02005028/* updates the server's weight during a warmup stage. Once the final weight is
5029 * reached, the task automatically stops. Note that any server status change
5030 * must have updated s->last_change accordingly.
5031 */
5032static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005033{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005034 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005035
Christopher Faulet61cc8522020-04-20 14:54:42 +02005036 /* by default, plan on stopping the task */
5037 t->expire = TICK_ETERNITY;
5038 if ((s->next_admin & SRV_ADMF_MAINT) ||
5039 (s->next_state != SRV_ST_STARTING))
5040 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005041
Christopher Faulet61cc8522020-04-20 14:54:42 +02005042 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005043
Christopher Faulet61cc8522020-04-20 14:54:42 +02005044 /* recalculate the weights and update the state */
5045 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005046
Christopher Faulet61cc8522020-04-20 14:54:42 +02005047 /* probably that we can refill this server with a bit more connections */
5048 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005049
Christopher Faulet61cc8522020-04-20 14:54:42 +02005050 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005051
Christopher Faulet61cc8522020-04-20 14:54:42 +02005052 /* get back there in 1 second or 1/20th of the slowstart interval,
5053 * whichever is greater, resulting in small 5% steps.
5054 */
5055 if (s->next_state == SRV_ST_STARTING)
5056 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5057 return t;
5058}
5059
5060/*
5061 * Start health-check.
5062 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5063 */
5064static int start_checks()
5065{
5066
5067 struct proxy *px;
5068 struct server *s;
5069 struct task *t;
5070 int nbcheck=0, mininter=0, srvpos=0;
5071
5072 /* 0- init the dummy frontend used to create all checks sessions */
5073 init_new_proxy(&checks_fe);
5074 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5075 checks_fe.mode = PR_MODE_TCP;
5076 checks_fe.maxconn = 0;
5077 checks_fe.conn_retries = CONN_RETRIES;
5078 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5079 checks_fe.timeout.client = TICK_ETERNITY;
5080
5081 /* 1- count the checkers to run simultaneously.
5082 * We also determine the minimum interval among all of those which
5083 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5084 * will be used to spread their start-up date. Those which have
5085 * a shorter interval will start independently and will not dictate
5086 * too short an interval for all others.
5087 */
5088 for (px = proxies_list; px; px = px->next) {
5089 for (s = px->srv; s; s = s->next) {
5090 if (s->slowstart) {
5091 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5092 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5093 return ERR_ALERT | ERR_FATAL;
5094 }
5095 /* We need a warmup task that will be called when the server
5096 * state switches from down to up.
5097 */
5098 s->warmup = t;
5099 t->process = server_warmup;
5100 t->context = s;
5101 /* server can be in this state only because of */
5102 if (s->next_state == SRV_ST_STARTING)
5103 task_schedule(s->warmup, tick_add(now_ms, MS_TO_TICKS(MAX(1000, (now.tv_sec - s->last_change)) / 20)));
Christopher Faulet5c288742020-03-31 08:15:58 +02005104 }
5105
Christopher Faulet61cc8522020-04-20 14:54:42 +02005106 if (s->check.state & CHK_ST_CONFIGURED) {
5107 nbcheck++;
5108 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5109 (!mininter || mininter > srv_getinter(&s->check)))
5110 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005111 }
5112
Christopher Faulet61cc8522020-04-20 14:54:42 +02005113 if (s->agent.state & CHK_ST_CONFIGURED) {
5114 nbcheck++;
5115 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5116 (!mininter || mininter > srv_getinter(&s->agent)))
5117 mininter = srv_getinter(&s->agent);
5118 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005119 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005120 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005121
Christopher Faulet61cc8522020-04-20 14:54:42 +02005122 if (!nbcheck)
5123 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005124
Christopher Faulet61cc8522020-04-20 14:54:42 +02005125 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005126
Christopher Faulet61cc8522020-04-20 14:54:42 +02005127 /*
5128 * 2- start them as far as possible from each others. For this, we will
5129 * start them after their interval set to the min interval divided by
5130 * the number of servers, weighted by the server's position in the list.
5131 */
5132 for (px = proxies_list; px; px = px->next) {
5133 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5134 if (init_pid_list()) {
5135 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5136 return ERR_ALERT | ERR_FATAL;
5137 }
5138 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005139
Christopher Faulet61cc8522020-04-20 14:54:42 +02005140 for (s = px->srv; s; s = s->next) {
5141 /* A task for the main check */
5142 if (s->check.state & CHK_ST_CONFIGURED) {
5143 if (s->check.type == PR_O2_EXT_CHK) {
5144 if (!prepare_external_check(&s->check))
5145 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005146 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005147 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5148 return ERR_ALERT | ERR_FATAL;
5149 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005150 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005151
Christopher Faulet61cc8522020-04-20 14:54:42 +02005152 /* A task for a auxiliary agent check */
5153 if (s->agent.state & CHK_ST_CONFIGURED) {
5154 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5155 return ERR_ALERT | ERR_FATAL;
5156 }
5157 srvpos++;
5158 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005159 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005160 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005161 return 0;
5162}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005163
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005164
Christopher Faulet61cc8522020-04-20 14:54:42 +02005165/*
5166 * Return value:
5167 * the port to be used for the health check
5168 * 0 in case no port could be found for the check
5169 */
5170static int srv_check_healthcheck_port(struct check *chk)
5171{
5172 int i = 0;
5173 struct server *srv = NULL;
5174
5175 srv = chk->server;
5176
5177 /* by default, we use the health check port ocnfigured */
5178 if (chk->port > 0)
5179 return chk->port;
5180
5181 /* try to get the port from check_core.addr if check.port not set */
5182 i = get_host_port(&chk->addr);
5183 if (i > 0)
5184 return i;
5185
5186 /* try to get the port from server address */
5187 /* prevent MAPPORTS from working at this point, since checks could
5188 * not be performed in such case (MAPPORTS impose a relative ports
5189 * based on live traffic)
5190 */
5191 if (srv->flags & SRV_F_MAPPORTS)
5192 return 0;
5193
5194 i = srv->svc_port; /* by default */
5195 if (i > 0)
5196 return i;
5197
5198 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005199}
5200
Christopher Faulet61cc8522020-04-20 14:54:42 +02005201/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5202 * if an error occurred.
5203 */
5204static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005205{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005206 const char *err;
5207 struct tcpcheck_rule *r;
5208 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005209
Christopher Faulet61cc8522020-04-20 14:54:42 +02005210 if (!srv->do_check)
5211 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005212
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005213
Christopher Faulet61cc8522020-04-20 14:54:42 +02005214 /* If neither a port nor an addr was specified and no check transport
5215 * layer is forced, then the transport layer used by the checks is the
5216 * same as for the production traffic. Otherwise we use raw_sock by
5217 * default, unless one is specified.
5218 */
5219 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5220 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5221 srv->check.use_ssl = srv->use_ssl;
5222 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005223 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005224 else if (srv->check.use_ssl == 1)
5225 srv->check.xprt = xprt_get(XPRT_SSL);
5226 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005227 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02005228 else if (srv->check.use_ssl == 1)
5229 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005230
Christopher Faulet12882cf2020-04-23 15:50:18 +02005231 /* Inherit the mux protocol from the server if not already defined for
5232 * the check
5233 */
5234 if (srv->mux_proto && !srv->check.mux_proto)
5235 srv->check.mux_proto = srv->mux_proto;
5236
Christopher Faulet61cc8522020-04-20 14:54:42 +02005237 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005238
Christopher Faulet61cc8522020-04-20 14:54:42 +02005239 /* We need at least a service port, a check port or the first tcp-check
5240 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5241 */
5242 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5243 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5244 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005245
Christopher Faulet61cc8522020-04-20 14:54:42 +02005246 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5247 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5248 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5249 ret |= ERR_ALERT | ERR_ABORT;
5250 goto out;
5251 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005252
Christopher Faulet61cc8522020-04-20 14:54:42 +02005253 /* search the first action (connect / send / expect) in the list */
5254 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5255 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5256 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5257 "nor tcp_check rule 'connect' with port information.\n",
5258 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5259 ret |= ERR_ALERT | ERR_ABORT;
5260 goto out;
5261 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005262
Christopher Faulet61cc8522020-04-20 14:54:42 +02005263 /* scan the tcp-check ruleset to ensure a port has been configured */
5264 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5265 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5266 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5267 "and a tcp_check rule 'connect' with no port information.\n",
5268 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5269 ret |= ERR_ALERT | ERR_ABORT;
5270 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005271 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005272 }
5273
Christopher Faulet61cc8522020-04-20 14:54:42 +02005274 init:
5275 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5276 struct tcpcheck_ruleset *rs = NULL;
5277 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5278 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005279
Christopher Faulet61cc8522020-04-20 14:54:42 +02005280 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5281 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005282
Christopher Faulet61cc8522020-04-20 14:54:42 +02005283 rs = find_tcpcheck_ruleset("*tcp-check");
5284 if (!rs) {
5285 rs = create_tcpcheck_ruleset("*tcp-check");
5286 if (rs == NULL) {
5287 ha_alert("config: %s '%s': out of memory.\n",
5288 proxy_type_str(srv->proxy), srv->proxy->id);
5289 ret |= ERR_ALERT | ERR_FATAL;
5290 goto out;
5291 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005292 }
5293
Christopher Faulet61cc8522020-04-20 14:54:42 +02005294 free_tcpcheck_vars(&rules->preset_vars);
5295 rules->list = &rs->rules;
5296 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005297 }
5298
Christopher Faulet61cc8522020-04-20 14:54:42 +02005299 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5300 if (err) {
5301 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5302 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5303 ret |= ERR_ALERT | ERR_ABORT;
5304 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005305 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005306 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5307 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005308
Christopher Faulet61cc8522020-04-20 14:54:42 +02005309 out:
5310 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005311}
5312
Christopher Faulet61cc8522020-04-20 14:54:42 +02005313/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5314 * if an error occurred.
5315 */
5316static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005317{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005318 struct tcpcheck_rule *chk;
5319 const char *err;
5320 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005321
Christopher Faulet61cc8522020-04-20 14:54:42 +02005322 if (!srv->do_agent)
5323 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005324
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005325 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005326 * implicit one is inserted before all others.
5327 */
5328 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5329 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5330 chk = calloc(1, sizeof(*chk));
5331 if (!chk) {
5332 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5333 " to agent-check for server '%s' (out of memory).\n",
5334 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5335 ret |= ERR_ALERT | ERR_FATAL;
5336 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005337 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005338 chk->action = TCPCHK_ACT_CONNECT;
5339 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5340 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005341 }
5342
Christopher Faulete5870d82020-04-15 11:32:03 +02005343
Christopher Faulet61cc8522020-04-20 14:54:42 +02005344 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5345 if (err) {
5346 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5347 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5348 ret |= ERR_ALERT | ERR_ABORT;
5349 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005350 }
5351
Christopher Faulet61cc8522020-04-20 14:54:42 +02005352 if (!srv->agent.inter)
5353 srv->agent.inter = srv->check.inter;
5354
5355 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5356 global.maxsock++;
5357
5358 out:
5359 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005360}
5361
Christopher Faulet61cc8522020-04-20 14:54:42 +02005362/* Check tcp-check health-check configuration for the proxy <px>. */
5363static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005364{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005365 struct tcpcheck_rule *chk, *back;
5366 char *comment = NULL, *errmsg = NULL;
5367 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5368 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005369
Christopher Faulet61cc8522020-04-20 14:54:42 +02005370 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5371 deinit_proxy_tcpcheck(px);
5372 goto out;
5373 }
5374
5375 free(px->check_command);
5376 free(px->check_path);
5377 px->check_command = px->check_path = NULL;
5378
5379 if (!px->tcpcheck_rules.list) {
5380 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5381 ret |= ERR_ALERT | ERR_FATAL;
5382 goto out;
5383 }
5384
5385 /* HTTP ruleset only : */
5386 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5387 struct tcpcheck_rule *next;
5388
5389 /* move remaining implicit send rule from "option httpchk" line to the right place.
5390 * If such rule exists, it must be the first one. In this case, the rule is moved
5391 * after the first connect rule, if any. Otherwise, nothing is done.
5392 */
5393 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5394 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5395 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5396 if (next && next->action == TCPCHK_ACT_CONNECT) {
5397 LIST_DEL(&chk->list);
5398 LIST_ADD(&next->list, &chk->list);
5399 chk->index = next->index;
5400 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005401 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005402
5403 /* add implicit expect rule if the last one is a send. It is inherited from previous
5404 * versions where the http expect rule was optional. Now it is possible to chained
5405 * send/expect rules but the last expect may still be implicit.
5406 */
5407 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5408 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005409 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005410 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5411 px->conf.file, px->conf.line, &errmsg);
5412 if (!next) {
5413 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5414 "(%s).\n", px->id, errmsg);
5415 free(errmsg);
5416 ret |= ERR_ALERT | ERR_FATAL;
5417 goto out;
5418 }
5419 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5420 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005421 }
5422 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005423
5424 /* For all ruleset: */
5425
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005426 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005427 * implicit one is inserted before all others.
5428 */
5429 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5430 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5431 chk = calloc(1, sizeof(*chk));
5432 if (!chk) {
5433 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5434 "(out of memory).\n", px->id);
5435 ret |= ERR_ALERT | ERR_FATAL;
5436 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005437 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005438 chk->action = TCPCHK_ACT_CONNECT;
5439 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5440 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5441 }
5442
5443 /* Remove all comment rules. To do so, when a such rule is found, the
5444 * comment is assigned to the following rule(s).
5445 */
5446 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5447 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5448 free(comment);
5449 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005450 }
5451
Christopher Faulet61cc8522020-04-20 14:54:42 +02005452 prev_action = chk->action;
5453 switch (chk->action) {
5454 case TCPCHK_ACT_COMMENT:
5455 free(comment);
5456 comment = chk->comment;
5457 LIST_DEL(&chk->list);
5458 free(chk);
5459 break;
5460 case TCPCHK_ACT_CONNECT:
5461 if (!chk->comment && comment)
5462 chk->comment = strdup(comment);
5463 /* fall though */
5464 case TCPCHK_ACT_ACTION_KW:
5465 free(comment);
5466 comment = NULL;
5467 break;
5468 case TCPCHK_ACT_SEND:
5469 case TCPCHK_ACT_EXPECT:
5470 if (!chk->comment && comment)
5471 chk->comment = strdup(comment);
5472 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005473 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005474 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005475 free(comment);
5476 comment = NULL;
5477
5478 out:
5479 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005480}
5481
Christopher Faulet61cc8522020-04-20 14:54:42 +02005482void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005483{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005484 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5485 px->tcpcheck_rules.flags = 0;
5486 px->tcpcheck_rules.list = NULL;
5487}
Christopher Faulete5870d82020-04-15 11:32:03 +02005488
Christopher Faulet61cc8522020-04-20 14:54:42 +02005489static void deinit_srv_check(struct server *srv)
5490{
5491 if (srv->check.state & CHK_ST_CONFIGURED)
5492 free_check(&srv->check);
5493 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5494 srv->do_check = 0;
5495}
Christopher Faulete5870d82020-04-15 11:32:03 +02005496
Christopher Faulet61cc8522020-04-20 14:54:42 +02005497
5498static void deinit_srv_agent_check(struct server *srv)
5499{
5500 if (srv->agent.tcpcheck_rules) {
5501 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5502 free(srv->agent.tcpcheck_rules);
5503 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005504 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005505
Christopher Faulet61cc8522020-04-20 14:54:42 +02005506 if (srv->agent.state & CHK_ST_CONFIGURED)
5507 free_check(&srv->agent);
5508
5509 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5510 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005511}
5512
Christopher Faulet61cc8522020-04-20 14:54:42 +02005513static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005514{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005515 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005516 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005517 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005518
Christopher Fauletd7cee712020-04-21 13:45:00 +02005519 node = ebpt_first(&shared_tcpchecks);
5520 while (node) {
5521 next = ebpt_next(node);
5522 ebpt_delete(node);
5523 free(node->key);
5524 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005525 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5526 LIST_DEL(&r->list);
5527 free_tcpcheck(r, 0);
5528 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005529 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005530 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005531 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005532}
Christopher Faulete5870d82020-04-15 11:32:03 +02005533
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005534
Christopher Faulet61cc8522020-04-20 14:54:42 +02005535REGISTER_POST_SERVER_CHECK(init_srv_check);
5536REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5537REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5538REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005539
Christopher Faulet61cc8522020-04-20 14:54:42 +02005540REGISTER_SERVER_DEINIT(deinit_srv_check);
5541REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5542REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5543REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005544
Christopher Faulet61cc8522020-04-20 14:54:42 +02005545/**************************************************************************/
5546/****************************** Email alerts ******************************/
5547/* NOTE: It may be pertinent to use an applet to handle email alerts */
5548/* instead of a tcp-check ruleset */
5549/**************************************************************************/
5550void email_alert_free(struct email_alert *alert)
5551{
5552 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005553
Christopher Faulet61cc8522020-04-20 14:54:42 +02005554 if (!alert)
5555 return;
5556
5557 if (alert->rules.list) {
5558 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5559 LIST_DEL(&rule->list);
5560 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005561 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005562 free_tcpcheck_vars(&alert->rules.preset_vars);
5563 free(alert->rules.list);
5564 alert->rules.list = NULL;
5565 }
5566 pool_free(pool_head_email_alert, alert);
5567}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005568
Christopher Faulet61cc8522020-04-20 14:54:42 +02005569static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5570{
5571 struct check *check = context;
5572 struct email_alertq *q;
5573 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005574
Christopher Faulet61cc8522020-04-20 14:54:42 +02005575 q = container_of(check, typeof(*q), check);
5576
5577 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5578 while (1) {
5579 if (!(check->state & CHK_ST_ENABLED)) {
5580 if (LIST_ISEMPTY(&q->email_alerts)) {
5581 /* All alerts processed, queue the task */
5582 t->expire = TICK_ETERNITY;
5583 task_queue(t);
5584 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005585 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005586
5587 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5588 LIST_DEL(&alert->list);
5589 t->expire = now_ms;
5590 check->tcpcheck_rules = &alert->rules;
5591 check->status = HCHK_STATUS_INI;
5592 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005593 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005594
5595 process_chk(t, context, state);
5596 if (check->state & CHK_ST_INPROGRESS)
5597 break;
5598
5599 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5600 email_alert_free(alert);
5601 check->tcpcheck_rules = NULL;
5602 check->server = NULL;
5603 check->state &= ~CHK_ST_ENABLED;
5604 }
5605 end:
5606 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5607 return t;
5608}
5609
5610/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5611 *
5612 * The function returns 1 in success case, otherwise, it returns 0 and err is
5613 * filled.
5614 */
5615int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5616{
5617 struct mailer *mailer;
5618 struct email_alertq *queues;
5619 const char *err_str;
5620 int i = 0;
5621
5622 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5623 memprintf(err, "out of memory while allocating mailer alerts queues");
5624 goto fail_no_queue;
5625 }
5626
5627 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5628 struct email_alertq *q = &queues[i];
5629 struct check *check = &q->check;
5630 struct task *t;
5631
5632 LIST_INIT(&q->email_alerts);
5633 HA_SPIN_INIT(&q->lock);
5634 check->inter = mls->timeout.mail;
5635 check->rise = DEF_AGENT_RISETIME;
5636 check->proxy = p;
5637 check->fall = DEF_AGENT_FALLTIME;
5638 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5639 memprintf(err, "%s", err_str);
5640 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005641 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005642
5643 check->xprt = mailer->xprt;
5644 check->addr = mailer->addr;
5645 check->port = get_host_port(&mailer->addr);
5646
5647 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5648 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005649 goto error;
5650 }
5651
Christopher Faulet61cc8522020-04-20 14:54:42 +02005652 check->task = t;
5653 t->process = process_email_alert;
5654 t->context = check;
5655
5656 /* check this in one ms */
5657 t->expire = TICK_ETERNITY;
5658 check->start = now;
5659 task_queue(t);
5660 }
5661
5662 mls->users++;
5663 free(p->email_alert.mailers.name);
5664 p->email_alert.mailers.m = mls;
5665 p->email_alert.queues = queues;
5666 return 0;
5667
5668 error:
5669 for (i = 0; i < mls->count; i++) {
5670 struct email_alertq *q = &queues[i];
5671 struct check *check = &q->check;
5672
5673 free_check(check);
5674 }
5675 free(queues);
5676 fail_no_queue:
5677 return 1;
5678}
5679
5680static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5681{
5682 struct tcpcheck_rule *tcpcheck, *prev_check;
5683 struct tcpcheck_expect *expect;
5684
5685 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5686 return 0;
5687 memset(tcpcheck, 0, sizeof(*tcpcheck));
5688 tcpcheck->action = TCPCHK_ACT_EXPECT;
5689
5690 expect = &tcpcheck->expect;
5691 expect->type = TCPCHK_EXPECT_STRING;
5692 LIST_INIT(&expect->onerror_fmt);
5693 LIST_INIT(&expect->onsuccess_fmt);
5694 expect->ok_status = HCHK_STATUS_L7OKD;
5695 expect->err_status = HCHK_STATUS_L7RSP;
5696 expect->tout_status = HCHK_STATUS_L7TOUT;
5697 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005698 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005699 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5700 return 0;
5701 }
5702
5703 /* All tcp-check expect points back to the first inverse expect rule
5704 * in a chain of one or more expect rule, potentially itself.
5705 */
5706 tcpcheck->expect.head = tcpcheck;
5707 list_for_each_entry_rev(prev_check, rules->list, list) {
5708 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5709 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5710 tcpcheck->expect.head = prev_check;
5711 continue;
5712 }
5713 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5714 break;
5715 }
5716 LIST_ADDQ(rules->list, &tcpcheck->list);
5717 return 1;
5718}
5719
5720static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5721{
5722 struct tcpcheck_rule *tcpcheck;
5723 struct tcpcheck_send *send;
5724 const char *in;
5725 char *dst;
5726 int i;
5727
5728 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5729 return 0;
5730 memset(tcpcheck, 0, sizeof(*tcpcheck));
5731 tcpcheck->action = TCPCHK_ACT_SEND;
5732
5733 send = &tcpcheck->send;
5734 send->type = TCPCHK_SEND_STRING;
5735
5736 for (i = 0; strs[i]; i++)
5737 send->data.len += strlen(strs[i]);
5738
Christopher Fauletb61caf42020-04-21 10:57:42 +02005739 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005740 if (!isttest(send->data)) {
5741 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5742 return 0;
5743 }
5744
Christopher Fauletb61caf42020-04-21 10:57:42 +02005745 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005746 for (i = 0; strs[i]; i++)
5747 for (in = strs[i]; (*dst = *in++); dst++);
5748 *dst = 0;
5749
5750 LIST_ADDQ(rules->list, &tcpcheck->list);
5751 return 1;
5752}
5753
5754static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5755 struct email_alertq *q, const char *msg)
5756{
5757 struct email_alert *alert;
5758 struct tcpcheck_rule *tcpcheck;
5759 struct check *check = &q->check;
5760
5761 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5762 goto error;
5763 LIST_INIT(&alert->list);
5764 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5765 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5766 if (!alert->rules.list)
5767 goto error;
5768 LIST_INIT(alert->rules.list);
5769 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5770 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005771
Christopher Faulet61cc8522020-04-20 14:54:42 +02005772 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5773 goto error;
5774 memset(tcpcheck, 0, sizeof(*tcpcheck));
5775 tcpcheck->action = TCPCHK_ACT_CONNECT;
5776 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005777
Christopher Faulet61cc8522020-04-20 14:54:42 +02005778 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005779
Christopher Faulet61cc8522020-04-20 14:54:42 +02005780 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005781 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005782
5783 {
5784 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5785 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5786 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005787 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005788
Christopher Faulet61cc8522020-04-20 14:54:42 +02005789 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5790 goto error;
5791
5792 {
5793 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5794 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005795 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005796 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005797
5798 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5799 goto error;
5800
5801 {
5802 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5803 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005804 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005805 }
5806
Christopher Faulet61cc8522020-04-20 14:54:42 +02005807 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5808 goto error;
5809
5810 {
5811 const char * const strs[2] = { "DATA\r\n" };
5812 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005813 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005814 }
5815
5816 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5817 goto error;
5818
5819 {
5820 struct tm tm;
5821 char datestr[48];
5822 const char * const strs[18] = {
5823 "From: ", p->email_alert.from, "\r\n",
5824 "To: ", p->email_alert.to, "\r\n",
5825 "Date: ", datestr, "\r\n",
5826 "Subject: [HAproxy Alert] ", msg, "\r\n",
5827 "\r\n",
5828 msg, "\r\n",
5829 "\r\n",
5830 ".\r\n",
5831 NULL
5832 };
5833
5834 get_localtime(date.tv_sec, &tm);
5835
5836 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005837 goto error;
5838 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005839
5840 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005841 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005842 }
5843
5844 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005845 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005846
5847 {
5848 const char * const strs[2] = { "QUIT\r\n" };
5849 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5850 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005851 }
5852
Christopher Faulet61cc8522020-04-20 14:54:42 +02005853 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5854 goto error;
5855
5856 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5857 task_wakeup(check->task, TASK_WOKEN_MSG);
5858 LIST_ADDQ(&q->email_alerts, &alert->list);
5859 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5860 return 1;
5861
5862error:
5863 email_alert_free(alert);
5864 return 0;
5865}
5866
5867static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5868{
5869 int i;
5870 struct mailer *mailer;
5871
5872 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5873 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5874 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5875 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5876 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005877 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005878 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005879
Christopher Faulet61cc8522020-04-20 14:54:42 +02005880 return;
5881}
5882
5883/*
5884 * Send email alert if configured.
5885 */
5886void send_email_alert(struct server *s, int level, const char *format, ...)
5887{
5888 va_list argp;
5889 char buf[1024];
5890 int len;
5891 struct proxy *p = s->proxy;
5892
5893 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5894 return;
5895
5896 va_start(argp, format);
5897 len = vsnprintf(buf, sizeof(buf), format, argp);
5898 va_end(argp);
5899
5900 if (len < 0 || len >= sizeof(buf)) {
5901 ha_alert("Email alert [%s] could not format message\n", p->id);
5902 return;
5903 }
5904
5905 enqueue_email_alert(p, s, buf);
5906}
5907
5908/**************************************************************************/
5909/************************** Check sample fetches **************************/
5910/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005911
Christopher Faulet61cc8522020-04-20 14:54:42 +02005912static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005913 { /* END */ },
5914}};
5915
5916INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5917
5918
5919/**************************************************************************/
5920/************************ Check's parsing functions ***********************/
5921/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005922/* Parses the "tcp-check" proxy keyword */
5923static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5924 struct proxy *defpx, const char *file, int line,
5925 char **errmsg)
5926{
Christopher Faulet404f9192020-04-09 23:13:54 +02005927 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005928 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005929 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005930
5931 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5932 ret = 1;
5933
Christopher Faulet404f9192020-04-09 23:13:54 +02005934 /* Deduce the ruleset name from the proxy info */
5935 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5936 ((curpx == defpx) ? "defaults" : curpx->id),
5937 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005938
Christopher Faulet61cc8522020-04-20 14:54:42 +02005939 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005940 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005941 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005942 if (rs == NULL) {
5943 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005944 goto error;
5945 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005946 }
5947
Gaetan Rivet5301b012020-02-25 17:19:17 +01005948 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005949 if (!LIST_ISEMPTY(&rs->rules)) {
5950 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005951 index = chk->index + 1;
5952 }
5953
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005954 cur_arg = 1;
5955 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005956 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005957 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5958 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005959 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005960 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005961 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005962 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005963 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005964 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005965 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5966
5967 if (!kw) {
5968 action_kw_tcp_check_build_list(&trash);
5969 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5970 "%s%s. but got '%s'",
5971 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5972 goto error;
5973 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005974 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005975 }
5976
5977 if (!chk) {
5978 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5979 goto error;
5980 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005981 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005982
5983 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005984 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005985 LIST_ADDQ(&rs->rules, &chk->list);
5986
5987 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005988 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005989 /* Use this ruleset if the proxy already has tcp-check enabled */
5990 curpx->tcpcheck_rules.list = &rs->rules;
5991 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5992 }
5993 else {
5994 /* mark this ruleset as unused for now */
5995 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
5996 }
5997
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005998 return ret;
5999
6000 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006001 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006002 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006003 return -1;
6004}
6005
Christopher Faulet51b129f2020-04-09 15:54:18 +02006006/* Parses the "http-check" proxy keyword */
6007static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6008 struct proxy *defpx, const char *file, int line,
6009 char **errmsg)
6010{
Christopher Faulete5870d82020-04-15 11:32:03 +02006011 struct tcpcheck_ruleset *rs = NULL;
6012 struct tcpcheck_rule *chk = NULL;
6013 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006014
6015 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6016 ret = 1;
6017
6018 cur_arg = 1;
6019 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6020 /* enable a graceful server shutdown on an HTTP 404 response */
6021 curpx->options |= PR_O_DISABLE404;
6022 if (too_many_args(1, args, errmsg, NULL))
6023 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006024 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006025 }
6026 else if (strcmp(args[cur_arg], "send-state") == 0) {
6027 /* enable emission of the apparent state of a server in HTTP checks */
6028 curpx->options2 |= PR_O2_CHK_SNDST;
6029 if (too_many_args(1, args, errmsg, NULL))
6030 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006031 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006032 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006033
Christopher Faulete5870d82020-04-15 11:32:03 +02006034 /* Deduce the ruleset name from the proxy info */
6035 chunk_printf(&trash, "*http-check-%s_%s-%d",
6036 ((curpx == defpx) ? "defaults" : curpx->id),
6037 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006038
Christopher Faulet61cc8522020-04-20 14:54:42 +02006039 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006040 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006041 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006042 if (rs == NULL) {
6043 memprintf(errmsg, "out of memory.\n");
6044 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006045 }
6046 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006047
Christopher Faulete5870d82020-04-15 11:32:03 +02006048 index = 0;
6049 if (!LIST_ISEMPTY(&rs->rules)) {
6050 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6051 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6052 index = chk->index + 1;
6053 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006054
Christopher Faulete5870d82020-04-15 11:32:03 +02006055 if (strcmp(args[cur_arg], "connect") == 0)
6056 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6057 else if (strcmp(args[cur_arg], "send") == 0)
6058 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6059 else if (strcmp(args[cur_arg], "expect") == 0)
6060 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6061 file, line, errmsg);
6062 else if (strcmp(args[cur_arg], "comment") == 0)
6063 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6064 else {
6065 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006066
Christopher Faulete5870d82020-04-15 11:32:03 +02006067 if (!kw) {
6068 action_kw_tcp_check_build_list(&trash);
6069 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6070 " 'send', 'expect'%s%s. but got '%s'",
6071 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6072 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006073 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006074 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6075 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006076
Christopher Faulete5870d82020-04-15 11:32:03 +02006077 if (!chk) {
6078 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6079 goto error;
6080 }
6081 ret = (*errmsg != NULL); /* Handle warning */
6082
6083 chk->index = index;
6084 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6085 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6086 /* Use this ruleset if the proxy already has http-check enabled */
6087 curpx->tcpcheck_rules.list = &rs->rules;
6088 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6089 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6090 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6091 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006092 goto error;
6093 }
6094 }
6095 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006096 /* mark this ruleset as unused for now */
6097 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6098 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006099 }
6100
Christopher Faulete5870d82020-04-15 11:32:03 +02006101 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006102 return ret;
6103
6104 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006105 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006106 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006107 return -1;
6108}
6109
Christopher Faulete9111b62020-04-09 18:12:08 +02006110/* Parses the "external-check" proxy keyword */
6111static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6112 struct proxy *defpx, const char *file, int line,
6113 char **errmsg)
6114{
6115 int cur_arg, ret = 0;
6116
6117 cur_arg = 1;
6118 if (!*(args[cur_arg])) {
6119 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6120 goto error;
6121 }
6122
6123 if (strcmp(args[cur_arg], "command") == 0) {
6124 if (too_many_args(2, args, errmsg, NULL))
6125 goto error;
6126 if (!*(args[cur_arg+1])) {
6127 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6128 goto error;
6129 }
6130 free(curpx->check_command);
6131 curpx->check_command = strdup(args[cur_arg+1]);
6132 }
6133 else if (strcmp(args[cur_arg], "path") == 0) {
6134 if (too_many_args(2, args, errmsg, NULL))
6135 goto error;
6136 if (!*(args[cur_arg+1])) {
6137 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6138 goto error;
6139 }
6140 free(curpx->check_path);
6141 curpx->check_path = strdup(args[cur_arg+1]);
6142 }
6143 else {
6144 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6145 args[0], args[1]);
6146 goto error;
6147 }
6148
6149 ret = (*errmsg != NULL); /* Handle warning */
6150 return ret;
6151
6152error:
6153 return -1;
6154}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006155
Christopher Faulet430e4802020-04-09 15:28:16 +02006156/* Parses the "option tcp-check" proxy keyword */
6157int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6158 const char *file, int line)
6159{
Christopher Faulet404f9192020-04-09 23:13:54 +02006160 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006161 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6162 int err_code = 0;
6163
6164 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6165 err_code |= ERR_WARN;
6166
6167 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6168 goto out;
6169
Christopher Faulet404f9192020-04-09 23:13:54 +02006170 curpx->options2 &= ~PR_O2_CHK_ANY;
6171 curpx->options2 |= PR_O2_TCPCHK_CHK;
6172
Christopher Fauletd7e63962020-04-17 20:15:59 +02006173 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006174 /* If a tcp-check rulesset is already set, do nothing */
6175 if (rules->list)
6176 goto out;
6177
6178 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6179 * get it.
6180 */
6181 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6182 goto curpx_ruleset;
6183
6184 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6185 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006186 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006187 if (rs)
6188 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006189 }
6190
Christopher Faulet404f9192020-04-09 23:13:54 +02006191 curpx_ruleset:
6192 /* Deduce the ruleset name from the proxy info */
6193 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6194 ((curpx == defpx) ? "defaults" : curpx->id),
6195 curpx->conf.file, curpx->conf.line);
6196
Christopher Faulet61cc8522020-04-20 14:54:42 +02006197 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006198 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006199 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006200 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006201 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6202 goto error;
6203 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006204 }
6205
Christopher Faulet404f9192020-04-09 23:13:54 +02006206 ruleset_found:
6207 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006208 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006209 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006210
6211 out:
6212 return err_code;
6213
6214 error:
6215 err_code |= ERR_ALERT | ERR_FATAL;
6216 goto out;
6217}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006218
6219/* Parses the "option redis-check" proxy keyword */
6220int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6221 const char *file, int line)
6222{
6223 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6224 static char *redis_res = "+PONG\r\n";
6225
6226 struct tcpcheck_ruleset *rs = NULL;
6227 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6228 struct tcpcheck_rule *chk;
6229 char *errmsg = NULL;
6230 int err_code = 0;
6231
6232 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6233 err_code |= ERR_WARN;
6234
6235 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6236 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006237
6238 curpx->options2 &= ~PR_O2_CHK_ANY;
6239 curpx->options2 |= PR_O2_TCPCHK_CHK;
6240
6241 free_tcpcheck_vars(&rules->preset_vars);
6242 rules->list = NULL;
6243 rules->flags = 0;
6244
Christopher Faulet61cc8522020-04-20 14:54:42 +02006245 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006246 if (rs)
6247 goto ruleset_found;
6248
Christopher Faulet61cc8522020-04-20 14:54:42 +02006249 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006250 if (rs == NULL) {
6251 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6252 goto error;
6253 }
6254
6255 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6256 1, curpx, &rs->rules, file, line, &errmsg);
6257 if (!chk) {
6258 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6259 goto error;
6260 }
6261 chk->index = 0;
6262 LIST_ADDQ(&rs->rules, &chk->list);
6263
6264 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6265 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006266 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006267 "on-success", "Redis server is ok",
6268 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006269 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006270 if (!chk) {
6271 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6272 goto error;
6273 }
6274 chk->index = 1;
6275 LIST_ADDQ(&rs->rules, &chk->list);
6276
Christopher Faulet33f05df2020-04-01 11:08:50 +02006277 ruleset_found:
6278 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006279 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006280
6281 out:
6282 free(errmsg);
6283 return err_code;
6284
6285 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006286 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006287 err_code |= ERR_ALERT | ERR_FATAL;
6288 goto out;
6289}
6290
Christopher Faulet811f78c2020-04-01 11:10:27 +02006291
6292/* Parses the "option ssl-hello-chk" proxy keyword */
6293int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6294 const char *file, int line)
6295{
6296 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6297 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6298 *
6299 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6300 */
6301 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006302 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006303 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6304 "0079" /* ContentLength : 0x79 bytes after this one */
6305 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6306 "000075" /* HandshakeLength : 0x75 bytes after this one */
6307 "0300" /* Hello Version : 0x0300 = v3 */
6308 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6309 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6310 "00" /* Session ID length : empty (no session ID) */
6311 "004E" /* Cipher Suite Length : 78 bytes after this one */
6312 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6313 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6314 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6315 "000D" "000E" "000F" "0010" /* various bit lengths, */
6316 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6317 "0015" "0016" "0017" "0018"
6318 "0019" "001A" "001B" "002F"
6319 "0030" "0031" "0032" "0033"
6320 "0034" "0035" "0036" "0037"
6321 "0038" "0039" "003A"
6322 "01" /* Compression Length : 0x01 = 1 byte for types */
6323 "00" /* Compression Type : 0x00 = NULL compression */
6324 };
6325
6326 struct tcpcheck_ruleset *rs = NULL;
6327 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6328 struct tcpcheck_rule *chk;
6329 char *errmsg = NULL;
6330 int err_code = 0;
6331
6332 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6333 err_code |= ERR_WARN;
6334
6335 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6336 goto out;
6337
Christopher Faulet811f78c2020-04-01 11:10:27 +02006338 curpx->options2 &= ~PR_O2_CHK_ANY;
6339 curpx->options2 |= PR_O2_TCPCHK_CHK;
6340
6341 free_tcpcheck_vars(&rules->preset_vars);
6342 rules->list = NULL;
6343 rules->flags = 0;
6344
Christopher Faulet61cc8522020-04-20 14:54:42 +02006345 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006346 if (rs)
6347 goto ruleset_found;
6348
Christopher Faulet61cc8522020-04-20 14:54:42 +02006349 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006350 if (rs == NULL) {
6351 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6352 goto error;
6353 }
6354
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006355 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006356 1, curpx, &rs->rules, file, line, &errmsg);
6357 if (!chk) {
6358 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6359 goto error;
6360 }
6361 chk->index = 0;
6362 LIST_ADDQ(&rs->rules, &chk->list);
6363
6364 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006365 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006366 "error-status", "L6RSP", "tout-status", "L6TOUT",
6367 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006368 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006369 if (!chk) {
6370 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6371 goto error;
6372 }
6373 chk->index = 1;
6374 LIST_ADDQ(&rs->rules, &chk->list);
6375
Christopher Faulet811f78c2020-04-01 11:10:27 +02006376 ruleset_found:
6377 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006378 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006379
6380 out:
6381 free(errmsg);
6382 return err_code;
6383
6384 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006385 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006386 err_code |= ERR_ALERT | ERR_FATAL;
6387 goto out;
6388}
6389
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006390/* Parses the "option smtpchk" proxy keyword */
6391int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6392 const char *file, int line)
6393{
6394 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6395
6396 struct tcpcheck_ruleset *rs = NULL;
6397 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6398 struct tcpcheck_rule *chk;
6399 struct tcpcheck_var *var = NULL;
6400 char *cmd = NULL, *errmsg = NULL;
6401 int err_code = 0;
6402
6403 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6404 err_code |= ERR_WARN;
6405
6406 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6407 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006408
6409 curpx->options2 &= ~PR_O2_CHK_ANY;
6410 curpx->options2 |= PR_O2_TCPCHK_CHK;
6411
6412 free_tcpcheck_vars(&rules->preset_vars);
6413 rules->list = NULL;
6414 rules->flags = 0;
6415
6416 cur_arg += 2;
6417 if (*args[cur_arg] && *args[cur_arg+1] &&
6418 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6419 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6420 if (cmd)
6421 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6422 }
6423 else {
6424 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6425 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6426 cmd = strdup("HELO localhost");
6427 }
6428
Christopher Fauletb61caf42020-04-21 10:57:42 +02006429 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006430 if (cmd == NULL || var == NULL) {
6431 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6432 goto error;
6433 }
6434 var->data.type = SMP_T_STR;
6435 var->data.u.str.area = cmd;
6436 var->data.u.str.data = strlen(cmd);
6437 LIST_INIT(&var->list);
6438 LIST_ADDQ(&rules->preset_vars, &var->list);
6439 cmd = NULL;
6440 var = NULL;
6441
Christopher Faulet61cc8522020-04-20 14:54:42 +02006442 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006443 if (rs)
6444 goto ruleset_found;
6445
Christopher Faulet61cc8522020-04-20 14:54:42 +02006446 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006447 if (rs == NULL) {
6448 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6449 goto error;
6450 }
6451
6452 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6453 1, curpx, &rs->rules, file, line, &errmsg);
6454 if (!chk) {
6455 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6456 goto error;
6457 }
6458 chk->index = 0;
6459 LIST_ADDQ(&rs->rules, &chk->list);
6460
6461 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6462 "min-recv", "4",
6463 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006464 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006465 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006466 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006467 if (!chk) {
6468 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6469 goto error;
6470 }
6471 chk->index = 1;
6472 LIST_ADDQ(&rs->rules, &chk->list);
6473
6474 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6475 "min-recv", "4",
6476 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006477 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6478 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006479 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006480 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006481 if (!chk) {
6482 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6483 goto error;
6484 }
6485 chk->index = 2;
6486 LIST_ADDQ(&rs->rules, &chk->list);
6487
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006488 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006489 1, curpx, &rs->rules, file, line, &errmsg);
6490 if (!chk) {
6491 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6492 goto error;
6493 }
6494 chk->index = 3;
6495 LIST_ADDQ(&rs->rules, &chk->list);
6496
6497 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6498 "min-recv", "4",
6499 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006500 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6501 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6502 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006503 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006504 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006505 if (!chk) {
6506 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6507 goto error;
6508 }
6509 chk->index = 4;
6510 LIST_ADDQ(&rs->rules, &chk->list);
6511
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006512 ruleset_found:
6513 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006514 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006515
6516 out:
6517 free(errmsg);
6518 return err_code;
6519
6520 error:
6521 free(cmd);
6522 free(var);
6523 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006524 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006525 err_code |= ERR_ALERT | ERR_FATAL;
6526 goto out;
6527}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006528
Christopher Fauletce355072020-04-02 11:44:39 +02006529/* Parses the "option pgsql-check" proxy keyword */
6530int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6531 const char *file, int line)
6532{
6533 static char pgsql_req[] = {
6534 "%[var(check.plen),htonl,hex]" /* The packet length*/
6535 "00030000" /* the version 3.0 */
6536 "7573657200" /* "user" key */
6537 "%[var(check.username),hex]00" /* the username */
6538 "00"
6539 };
6540
6541 struct tcpcheck_ruleset *rs = NULL;
6542 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6543 struct tcpcheck_rule *chk;
6544 struct tcpcheck_var *var = NULL;
6545 char *user = NULL, *errmsg = NULL;
6546 size_t packetlen = 0;
6547 int err_code = 0;
6548
6549 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6550 err_code |= ERR_WARN;
6551
6552 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6553 goto out;
6554
Christopher Fauletce355072020-04-02 11:44:39 +02006555 curpx->options2 &= ~PR_O2_CHK_ANY;
6556 curpx->options2 |= PR_O2_TCPCHK_CHK;
6557
6558 free_tcpcheck_vars(&rules->preset_vars);
6559 rules->list = NULL;
6560 rules->flags = 0;
6561
6562 cur_arg += 2;
6563 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6564 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6565 file, line, args[0], args[1]);
6566 goto error;
6567 }
6568 if (strcmp(args[cur_arg], "user") == 0) {
6569 packetlen = 15 + strlen(args[cur_arg+1]);
6570 user = strdup(args[cur_arg+1]);
6571
Christopher Fauletb61caf42020-04-21 10:57:42 +02006572 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006573 if (user == NULL || var == NULL) {
6574 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6575 goto error;
6576 }
6577 var->data.type = SMP_T_STR;
6578 var->data.u.str.area = user;
6579 var->data.u.str.data = strlen(user);
6580 LIST_INIT(&var->list);
6581 LIST_ADDQ(&rules->preset_vars, &var->list);
6582 user = NULL;
6583 var = NULL;
6584
Christopher Fauletb61caf42020-04-21 10:57:42 +02006585 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006586 if (var == NULL) {
6587 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6588 goto error;
6589 }
6590 var->data.type = SMP_T_SINT;
6591 var->data.u.sint = packetlen;
6592 LIST_INIT(&var->list);
6593 LIST_ADDQ(&rules->preset_vars, &var->list);
6594 var = NULL;
6595 }
6596 else {
6597 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6598 file, line, args[0], args[1]);
6599 goto error;
6600 }
6601
Christopher Faulet61cc8522020-04-20 14:54:42 +02006602 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006603 if (rs)
6604 goto ruleset_found;
6605
Christopher Faulet61cc8522020-04-20 14:54:42 +02006606 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006607 if (rs == NULL) {
6608 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6609 goto error;
6610 }
6611
6612 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6613 1, curpx, &rs->rules, file, line, &errmsg);
6614 if (!chk) {
6615 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6616 goto error;
6617 }
6618 chk->index = 0;
6619 LIST_ADDQ(&rs->rules, &chk->list);
6620
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006621 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006622 1, curpx, &rs->rules, file, line, &errmsg);
6623 if (!chk) {
6624 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6625 goto error;
6626 }
6627 chk->index = 1;
6628 LIST_ADDQ(&rs->rules, &chk->list);
6629
6630 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6631 "min-recv", "5",
6632 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006633 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006634 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006635 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006636 if (!chk) {
6637 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6638 goto error;
6639 }
6640 chk->index = 2;
6641 LIST_ADDQ(&rs->rules, &chk->list);
6642
Christopher Fauletb841c742020-04-27 18:29:49 +02006643 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^52000000(08|0A|0C)000000(00|02|03|04|05|06)",
Christopher Fauletce355072020-04-02 11:44:39 +02006644 "min-recv", "9",
6645 "error-status", "L7STS",
6646 "on-success", "PostgreSQL server is ok",
6647 "on-error", "PostgreSQL unknown error",
6648 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006649 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006650 if (!chk) {
6651 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6652 goto error;
6653 }
6654 chk->index = 3;
6655 LIST_ADDQ(&rs->rules, &chk->list);
6656
Christopher Fauletce355072020-04-02 11:44:39 +02006657 ruleset_found:
6658 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006659 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006660
6661 out:
6662 free(errmsg);
6663 return err_code;
6664
6665 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006666 free(user);
6667 free(var);
6668 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006669 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006670 err_code |= ERR_ALERT | ERR_FATAL;
6671 goto out;
6672}
6673
6674
6675/* Parses the "option mysql-check" proxy keyword */
6676int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6677 const char *file, int line)
6678{
6679 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6680 * const char mysql40_client_auth_pkt[] = {
6681 * "\x0e\x00\x00" // packet length
6682 * "\x01" // packet number
6683 * "\x00\x00" // client capabilities
6684 * "\x00\x00\x01" // max packet
6685 * "haproxy\x00" // username (null terminated string)
6686 * "\x00" // filler (always 0x00)
6687 * "\x01\x00\x00" // packet length
6688 * "\x00" // packet number
6689 * "\x01" // COM_QUIT command
6690 * };
6691 */
6692 static char mysql40_rsname[] = "*mysql40-check";
6693 static char mysql40_req[] = {
6694 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6695 "0080" /* client capabilities */
6696 "000001" /* max packet */
6697 "%[var(check.username),hex]00" /* the username */
6698 "00" /* filler (always 0x00) */
6699 "010000" /* packet length*/
6700 "00" /* sequence ID */
6701 "01" /* COM_QUIT command */
6702 };
6703
6704 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6705 * const char mysql41_client_auth_pkt[] = {
6706 * "\x0e\x00\x00\" // packet length
6707 * "\x01" // packet number
6708 * "\x00\x00\x00\x00" // client capabilities
6709 * "\x00\x00\x00\x01" // max packet
6710 * "\x21" // character set (UTF-8)
6711 * char[23] // All zeroes
6712 * "haproxy\x00" // username (null terminated string)
6713 * "\x00" // filler (always 0x00)
6714 * "\x01\x00\x00" // packet length
6715 * "\x00" // packet number
6716 * "\x01" // COM_QUIT command
6717 * };
6718 */
6719 static char mysql41_rsname[] = "*mysql41-check";
6720 static char mysql41_req[] = {
6721 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6722 "00820000" /* client capabilities */
6723 "00800001" /* max packet */
6724 "21" /* character set (UTF-8) */
6725 "000000000000000000000000" /* 23 bytes, al zeroes */
6726 "0000000000000000000000"
6727 "%[var(check.username),hex]00" /* the username */
6728 "00" /* filler (always 0x00) */
6729 "010000" /* packet length*/
6730 "00" /* sequence ID */
6731 "01" /* COM_QUIT command */
6732 };
6733
6734 struct tcpcheck_ruleset *rs = NULL;
6735 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6736 struct tcpcheck_rule *chk;
6737 struct tcpcheck_var *var = NULL;
6738 char *mysql_rsname = "*mysql-check";
6739 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6740 int index = 0, err_code = 0;
6741
6742 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6743 err_code |= ERR_WARN;
6744
6745 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6746 goto out;
6747
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006748 curpx->options2 &= ~PR_O2_CHK_ANY;
6749 curpx->options2 |= PR_O2_TCPCHK_CHK;
6750
6751 free_tcpcheck_vars(&rules->preset_vars);
6752 rules->list = NULL;
6753 rules->flags = 0;
6754
6755 cur_arg += 2;
6756 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006757 int packetlen, userlen;
6758
6759 if (strcmp(args[cur_arg], "user") != 0) {
6760 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6761 file, line, args[0], args[1], args[cur_arg]);
6762 goto error;
6763 }
6764
6765 if (*(args[cur_arg+1]) == 0) {
6766 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6767 file, line, args[0], args[1], args[cur_arg]);
6768 goto error;
6769 }
6770
6771 hdr = calloc(4, sizeof(*hdr));
6772 user = strdup(args[cur_arg+1]);
6773 userlen = strlen(args[cur_arg+1]);
6774
6775 if (hdr == NULL || user == NULL) {
6776 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6777 goto error;
6778 }
6779
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006780 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006781 packetlen = userlen + 7 + 27;
6782 mysql_req = mysql41_req;
6783 mysql_rsname = mysql41_rsname;
6784 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006785 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006786 packetlen = userlen + 7;
6787 mysql_req = mysql40_req;
6788 mysql_rsname = mysql40_rsname;
6789 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006790 else {
6791 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
6792 file, line, args[cur_arg], args[cur_arg+2]);
6793 goto error;
6794 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006795
6796 hdr[0] = (unsigned char)(packetlen & 0xff);
6797 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6798 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6799 hdr[3] = 1;
6800
Christopher Fauletb61caf42020-04-21 10:57:42 +02006801 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006802 if (var == NULL) {
6803 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6804 goto error;
6805 }
6806 var->data.type = SMP_T_STR;
6807 var->data.u.str.area = hdr;
6808 var->data.u.str.data = 4;
6809 LIST_INIT(&var->list);
6810 LIST_ADDQ(&rules->preset_vars, &var->list);
6811 hdr = NULL;
6812 var = NULL;
6813
Christopher Fauletb61caf42020-04-21 10:57:42 +02006814 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006815 if (var == NULL) {
6816 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6817 goto error;
6818 }
6819 var->data.type = SMP_T_STR;
6820 var->data.u.str.area = user;
6821 var->data.u.str.data = strlen(user);
6822 LIST_INIT(&var->list);
6823 LIST_ADDQ(&rules->preset_vars, &var->list);
6824 user = NULL;
6825 var = NULL;
6826 }
6827
Christopher Faulet61cc8522020-04-20 14:54:42 +02006828 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006829 if (rs)
6830 goto ruleset_found;
6831
Christopher Faulet61cc8522020-04-20 14:54:42 +02006832 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006833 if (rs == NULL) {
6834 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6835 goto error;
6836 }
6837
6838 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6839 1, curpx, &rs->rules, file, line, &errmsg);
6840 if (!chk) {
6841 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6842 goto error;
6843 }
6844 chk->index = index++;
6845 LIST_ADDQ(&rs->rules, &chk->list);
6846
6847 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006848 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006849 1, curpx, &rs->rules, file, line, &errmsg);
6850 if (!chk) {
6851 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6852 goto error;
6853 }
6854 chk->index = index++;
6855 LIST_ADDQ(&rs->rules, &chk->list);
6856 }
6857
6858 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006859 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006860 if (!chk) {
6861 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6862 goto error;
6863 }
6864 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6865 chk->index = index++;
6866 LIST_ADDQ(&rs->rules, &chk->list);
6867
6868 if (mysql_req) {
6869 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006870 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006871 if (!chk) {
6872 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6873 goto error;
6874 }
6875 chk->expect.custom = tcpcheck_mysql_expect_ok;
6876 chk->index = index++;
6877 LIST_ADDQ(&rs->rules, &chk->list);
6878 }
6879
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006880 ruleset_found:
6881 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006882 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006883
6884 out:
6885 free(errmsg);
6886 return err_code;
6887
6888 error:
6889 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006890 free(user);
6891 free(var);
6892 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006893 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006894 err_code |= ERR_ALERT | ERR_FATAL;
6895 goto out;
6896}
6897
Christopher Faulet1997eca2020-04-03 23:13:50 +02006898int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6899 const char *file, int line)
6900{
6901 static char *ldap_req = "300C020101600702010304008000";
6902
6903 struct tcpcheck_ruleset *rs = NULL;
6904 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6905 struct tcpcheck_rule *chk;
6906 char *errmsg = NULL;
6907 int err_code = 0;
6908
6909 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6910 err_code |= ERR_WARN;
6911
6912 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6913 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006914
6915 curpx->options2 &= ~PR_O2_CHK_ANY;
6916 curpx->options2 |= PR_O2_TCPCHK_CHK;
6917
6918 free_tcpcheck_vars(&rules->preset_vars);
6919 rules->list = NULL;
6920 rules->flags = 0;
6921
Christopher Faulet61cc8522020-04-20 14:54:42 +02006922 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006923 if (rs)
6924 goto ruleset_found;
6925
Christopher Faulet61cc8522020-04-20 14:54:42 +02006926 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006927 if (rs == NULL) {
6928 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6929 goto error;
6930 }
6931
6932 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6933 1, curpx, &rs->rules, file, line, &errmsg);
6934 if (!chk) {
6935 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6936 goto error;
6937 }
6938 chk->index = 0;
6939 LIST_ADDQ(&rs->rules, &chk->list);
6940
6941 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6942 "min-recv", "14",
6943 "on-error", "Not LDAPv3 protocol",
6944 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006945 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006946 if (!chk) {
6947 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6948 goto error;
6949 }
6950 chk->index = 1;
6951 LIST_ADDQ(&rs->rules, &chk->list);
6952
6953 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006954 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006955 if (!chk) {
6956 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6957 goto error;
6958 }
6959 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6960 chk->index = 2;
6961 LIST_ADDQ(&rs->rules, &chk->list);
6962
Christopher Faulet1997eca2020-04-03 23:13:50 +02006963 ruleset_found:
6964 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006965 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006966
6967 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006968 free(errmsg);
6969 return err_code;
6970
6971 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006972 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006973 err_code |= ERR_ALERT | ERR_FATAL;
6974 goto out;
6975}
6976
6977int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6978 const char *file, int line)
6979{
6980 struct tcpcheck_ruleset *rs = NULL;
6981 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6982 struct tcpcheck_rule *chk;
6983 char *spop_req = NULL;
6984 char *errmsg = NULL;
6985 int spop_len = 0, err_code = 0;
6986
6987 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6988 err_code |= ERR_WARN;
6989
6990 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6991 goto out;
6992
Christopher Faulet267b01b2020-04-04 10:27:09 +02006993 curpx->options2 &= ~PR_O2_CHK_ANY;
6994 curpx->options2 |= PR_O2_TCPCHK_CHK;
6995
6996 free_tcpcheck_vars(&rules->preset_vars);
6997 rules->list = NULL;
6998 rules->flags = 0;
6999
7000
Christopher Faulet61cc8522020-04-20 14:54:42 +02007001 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007002 if (rs)
7003 goto ruleset_found;
7004
Christopher Faulet61cc8522020-04-20 14:54:42 +02007005 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007006 if (rs == NULL) {
7007 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7008 goto error;
7009 }
7010
7011 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7012 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7013 goto error;
7014 }
7015 chunk_reset(&trash);
7016 dump_binary(&trash, spop_req, spop_len);
7017 trash.area[trash.data] = '\0';
7018
7019 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7020 1, curpx, &rs->rules, file, line, &errmsg);
7021 if (!chk) {
7022 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7023 goto error;
7024 }
7025 chk->index = 0;
7026 LIST_ADDQ(&rs->rules, &chk->list);
7027
7028 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007029 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007030 if (!chk) {
7031 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7032 goto error;
7033 }
7034 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7035 chk->index = 1;
7036 LIST_ADDQ(&rs->rules, &chk->list);
7037
Christopher Faulet267b01b2020-04-04 10:27:09 +02007038 ruleset_found:
7039 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007040 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007041
7042 out:
7043 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007044 free(errmsg);
7045 return err_code;
7046
7047 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007048 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007049 err_code |= ERR_ALERT | ERR_FATAL;
7050 goto out;
7051}
Christopher Fauletce355072020-04-02 11:44:39 +02007052
Christopher Faulete5870d82020-04-15 11:32:03 +02007053
7054struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7055{
7056 struct tcpcheck_rule *chk = NULL;
7057 struct tcpcheck_http_hdr *hdr = NULL;
7058 char *meth = NULL, *uri = NULL, *vsn = NULL;
7059 char *hdrs, *body;
7060
7061 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7062 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7063 if (hdrs == body)
7064 hdrs = NULL;
7065 if (hdrs) {
7066 *hdrs = '\0';
7067 hdrs +=2;
7068 }
7069 if (body) {
7070 *body = '\0';
7071 body += 4;
7072 }
7073 if (hdrs || body) {
7074 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7075 " Please, consider to use 'http-check send' directive instead.");
7076 }
7077
7078 chk = calloc(1, sizeof(*chk));
7079 if (!chk) {
7080 memprintf(errmsg, "out of memory");
7081 goto error;
7082 }
7083 chk->action = TCPCHK_ACT_SEND;
7084 chk->send.type = TCPCHK_SEND_HTTP;
7085 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7086 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7087 LIST_INIT(&chk->send.http.hdrs);
7088
7089 /* Copy the method, uri and version */
7090 if (*args[cur_arg]) {
7091 if (!*args[cur_arg+1])
7092 uri = args[cur_arg];
7093 else
7094 meth = args[cur_arg];
7095 }
7096 if (*args[cur_arg+1])
7097 uri = args[cur_arg+1];
7098 if (*args[cur_arg+2])
7099 vsn = args[cur_arg+2];
7100
7101 if (meth) {
7102 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7103 chk->send.http.meth.str.area = strdup(meth);
7104 chk->send.http.meth.str.data = strlen(meth);
7105 if (!chk->send.http.meth.str.area) {
7106 memprintf(errmsg, "out of memory");
7107 goto error;
7108 }
7109 }
7110 if (uri) {
7111 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007112 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007113 memprintf(errmsg, "out of memory");
7114 goto error;
7115 }
7116 }
7117 if (vsn) {
7118 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007119 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007120 memprintf(errmsg, "out of memory");
7121 goto error;
7122 }
7123 }
7124
7125 /* Copy the header */
7126 if (hdrs) {
7127 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7128 struct h1m h1m;
7129 int i, ret;
7130
7131 /* Build and parse the request */
7132 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7133
7134 h1m.flags = H1_MF_HDRS_ONLY;
7135 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7136 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7137 &h1m, NULL);
7138 if (ret <= 0) {
7139 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7140 goto error;
7141 }
7142
Christopher Fauletb61caf42020-04-21 10:57:42 +02007143 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007144 hdr = calloc(1, sizeof(*hdr));
7145 if (!hdr) {
7146 memprintf(errmsg, "out of memory");
7147 goto error;
7148 }
7149 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007150 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007151 if (!hdr->name.ptr) {
7152 memprintf(errmsg, "out of memory");
7153 goto error;
7154 }
7155
Christopher Fauletb61caf42020-04-21 10:57:42 +02007156 ist0(tmp_hdrs[i].v);
7157 if (!parse_logformat_string(istptr(tmp_hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
Christopher Faulete5870d82020-04-15 11:32:03 +02007158 goto error;
7159 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7160 }
7161 }
7162
7163 /* Copy the body */
7164 if (body) {
7165 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007166 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007167 memprintf(errmsg, "out of memory");
7168 goto error;
7169 }
7170 }
7171
7172 return chk;
7173
7174 error:
7175 free_tcpcheck_http_hdr(hdr);
7176 free_tcpcheck(chk, 0);
7177 return NULL;
7178}
7179
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007180int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7181 const char *file, int line)
7182{
Christopher Faulete5870d82020-04-15 11:32:03 +02007183 struct tcpcheck_ruleset *rs = NULL;
7184 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7185 struct tcpcheck_rule *chk;
7186 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007187 int err_code = 0;
7188
7189 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7190 err_code |= ERR_WARN;
7191
7192 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7193 goto out;
7194
Christopher Faulete5870d82020-04-15 11:32:03 +02007195 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7196 if (!chk) {
7197 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7198 goto error;
7199 }
7200 if (errmsg) {
7201 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7202 err_code |= ERR_WARN;
7203 free(errmsg);
7204 errmsg = NULL;
7205 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007206
Christopher Faulete5870d82020-04-15 11:32:03 +02007207 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007208 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007209 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007210
Christopher Faulete5870d82020-04-15 11:32:03 +02007211 free_tcpcheck_vars(&rules->preset_vars);
7212 rules->list = NULL;
7213 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007214
Christopher Faulete5870d82020-04-15 11:32:03 +02007215 /* Deduce the ruleset name from the proxy info */
7216 chunk_printf(&trash, "*http-check-%s_%s-%d",
7217 ((curpx == defpx) ? "defaults" : curpx->id),
7218 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007219
Christopher Faulet61cc8522020-04-20 14:54:42 +02007220 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007221 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007222 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007223 if (rs == NULL) {
7224 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7225 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007226 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007227 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007228
Christopher Faulete5870d82020-04-15 11:32:03 +02007229 rules->list = &rs->rules;
7230 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7231 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7232 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7233 rules->list = NULL;
7234 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007235 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007236
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007237 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007238 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007239 return err_code;
7240
7241 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007242 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007243 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007244 err_code |= ERR_ALERT | ERR_FATAL;
7245 goto out;
7246}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007247
Christopher Faulet6f557912020-04-09 15:58:50 +02007248int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7249 const char *file, int line)
7250{
7251 int err_code = 0;
7252
Christopher Faulet6f557912020-04-09 15:58:50 +02007253 curpx->options2 &= ~PR_O2_CHK_ANY;
7254 curpx->options2 |= PR_O2_EXT_CHK;
7255 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7256 goto out;
7257
7258 out:
7259 return err_code;
7260}
7261
Christopher Fauletce8111e2020-04-06 15:04:11 +02007262/* Parse the "addr" server keyword */
7263static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7264 char **errmsg)
7265{
7266 struct sockaddr_storage *sk;
7267 struct protocol *proto;
7268 int port1, port2, err_code = 0;
7269
7270
7271 if (!*args[*cur_arg+1]) {
7272 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7273 goto error;
7274 }
7275
7276 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7277 if (!sk) {
7278 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7279 goto error;
7280 }
7281
7282 proto = protocol_by_family(sk->ss_family);
7283 if (!proto || !proto->connect) {
7284 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7285 args[*cur_arg], args[*cur_arg+1]);
7286 goto error;
7287 }
7288
7289 if (port1 != port2) {
7290 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7291 args[*cur_arg], args[*cur_arg+1]);
7292 goto error;
7293 }
7294
7295 srv->check.addr = srv->agent.addr = *sk;
7296 srv->flags |= SRV_F_CHECKADDR;
7297 srv->flags |= SRV_F_AGENTADDR;
7298
7299 out:
7300 return err_code;
7301
7302 error:
7303 err_code |= ERR_ALERT | ERR_FATAL;
7304 goto out;
7305}
7306
7307
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007308/* Parse the "agent-addr" server keyword */
7309static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7310 char **errmsg)
7311{
7312 int err_code = 0;
7313
7314 if (!*(args[*cur_arg+1])) {
7315 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7316 goto error;
7317 }
7318 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7319 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7320 goto error;
7321 }
7322
7323 out:
7324 return err_code;
7325
7326 error:
7327 err_code |= ERR_ALERT | ERR_FATAL;
7328 goto out;
7329}
7330
7331/* Parse the "agent-check" server keyword */
7332static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7333 char **errmsg)
7334{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007335 struct tcpcheck_ruleset *rs = NULL;
7336 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7337 struct tcpcheck_rule *chk;
7338 int err_code = 0;
7339
7340 if (srv->do_agent)
7341 goto out;
7342
7343 if (!rules) {
7344 rules = calloc(1, sizeof(*rules));
7345 if (!rules) {
7346 memprintf(errmsg, "out of memory.");
7347 goto error;
7348 }
7349 LIST_INIT(&rules->preset_vars);
7350 srv->agent.tcpcheck_rules = rules;
7351 }
7352 rules->list = NULL;
7353 rules->flags = 0;
7354
Christopher Faulet61cc8522020-04-20 14:54:42 +02007355 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007356 if (rs)
7357 goto ruleset_found;
7358
Christopher Faulet61cc8522020-04-20 14:54:42 +02007359 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007360 if (rs == NULL) {
7361 memprintf(errmsg, "out of memory.");
7362 goto error;
7363 }
7364
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007365 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007366 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7367 if (!chk) {
7368 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7369 goto error;
7370 }
7371 chk->index = 0;
7372 LIST_ADDQ(&rs->rules, &chk->list);
7373
7374 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007375 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7376 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007377 if (!chk) {
7378 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7379 goto error;
7380 }
7381 chk->expect.custom = tcpcheck_agent_expect_reply;
7382 chk->index = 1;
7383 LIST_ADDQ(&rs->rules, &chk->list);
7384
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007385 ruleset_found:
7386 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007387 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007388 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007389
7390 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007391 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007392
7393 error:
7394 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007395 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007396 err_code |= ERR_ALERT | ERR_FATAL;
7397 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007398}
7399
7400/* Parse the "agent-inter" server keyword */
7401static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7402 char **errmsg)
7403{
7404 const char *err = NULL;
7405 unsigned int delay;
7406 int err_code = 0;
7407
7408 if (!*(args[*cur_arg+1])) {
7409 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7410 goto error;
7411 }
7412
7413 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7414 if (err == PARSE_TIME_OVER) {
7415 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7416 args[*cur_arg+1], args[*cur_arg], srv->id);
7417 goto error;
7418 }
7419 else if (err == PARSE_TIME_UNDER) {
7420 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7421 args[*cur_arg+1], args[*cur_arg], srv->id);
7422 goto error;
7423 }
7424 else if (err) {
7425 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7426 *err, srv->id);
7427 goto error;
7428 }
7429 if (delay <= 0) {
7430 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7431 delay, args[*cur_arg], srv->id);
7432 goto error;
7433 }
7434 srv->agent.inter = delay;
7435
7436 out:
7437 return err_code;
7438
7439 error:
7440 err_code |= ERR_ALERT | ERR_FATAL;
7441 goto out;
7442}
7443
7444/* Parse the "agent-port" server keyword */
7445static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7446 char **errmsg)
7447{
7448 int err_code = 0;
7449
7450 if (!*(args[*cur_arg+1])) {
7451 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7452 goto error;
7453 }
7454
7455 global.maxsock++;
7456 srv->agent.port = atol(args[*cur_arg+1]);
7457
7458 out:
7459 return err_code;
7460
7461 error:
7462 err_code |= ERR_ALERT | ERR_FATAL;
7463 goto out;
7464}
7465
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007466int set_srv_agent_send(struct server *srv, const char *send)
7467{
7468 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7469 struct tcpcheck_var *var = NULL;
7470 char *str;
7471
7472 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007473 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007474 if (str == NULL || var == NULL)
7475 goto error;
7476
7477 free_tcpcheck_vars(&rules->preset_vars);
7478
7479 var->data.type = SMP_T_STR;
7480 var->data.u.str.area = str;
7481 var->data.u.str.data = strlen(str);
7482 LIST_INIT(&var->list);
7483 LIST_ADDQ(&rules->preset_vars, &var->list);
7484
7485 return 1;
7486
7487 error:
7488 free(str);
7489 free(var);
7490 return 0;
7491}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007492
7493/* Parse the "agent-send" server keyword */
7494static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7495 char **errmsg)
7496{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007497 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007498 int err_code = 0;
7499
7500 if (!*(args[*cur_arg+1])) {
7501 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7502 goto error;
7503 }
7504
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007505 if (!rules) {
7506 rules = calloc(1, sizeof(*rules));
7507 if (!rules) {
7508 memprintf(errmsg, "out of memory.");
7509 goto error;
7510 }
7511 LIST_INIT(&rules->preset_vars);
7512 srv->agent.tcpcheck_rules = rules;
7513 }
7514
7515 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007516 memprintf(errmsg, "out of memory.");
7517 goto error;
7518 }
7519
7520 out:
7521 return err_code;
7522
7523 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007524 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007525 err_code |= ERR_ALERT | ERR_FATAL;
7526 goto out;
7527}
7528
7529/* Parse the "no-agent-send" server keyword */
7530static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7531 char **errmsg)
7532{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007533 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007534 return 0;
7535}
7536
Christopher Fauletce8111e2020-04-06 15:04:11 +02007537/* Parse the "check" server keyword */
7538static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7539 char **errmsg)
7540{
7541 srv->do_check = 1;
7542 return 0;
7543}
7544
7545/* Parse the "check-send-proxy" server keyword */
7546static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7547 char **errmsg)
7548{
7549 srv->check.send_proxy = 1;
7550 return 0;
7551}
7552
7553/* Parse the "check-via-socks4" server keyword */
7554static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7555 char **errmsg)
7556{
7557 srv->check.via_socks4 = 1;
7558 return 0;
7559}
7560
7561/* Parse the "no-check" server keyword */
7562static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7563 char **errmsg)
7564{
7565 deinit_srv_check(srv);
7566 return 0;
7567}
7568
7569/* Parse the "no-check-send-proxy" server keyword */
7570static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7571 char **errmsg)
7572{
7573 srv->check.send_proxy = 0;
7574 return 0;
7575}
7576
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007577/* parse the "check-proto" server keyword */
7578static int srv_parse_check_proto(char **args, int *cur_arg,
7579 struct proxy *px, struct server *newsrv, char **err)
7580{
7581 int err_code = 0;
7582
7583 if (!*args[*cur_arg + 1]) {
7584 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7585 goto error;
7586 }
7587 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7588 if (!newsrv->check.mux_proto) {
7589 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7590 goto error;
7591 }
7592
7593 out:
7594 return err_code;
7595
7596 error:
7597 err_code |= ERR_ALERT | ERR_FATAL;
7598 goto out;
7599}
7600
7601
Christopher Fauletce8111e2020-04-06 15:04:11 +02007602/* Parse the "rise" server keyword */
7603static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7604 char **errmsg)
7605{
7606 int err_code = 0;
7607
7608 if (!*args[*cur_arg + 1]) {
7609 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7610 goto error;
7611 }
7612
7613 srv->check.rise = atol(args[*cur_arg+1]);
7614 if (srv->check.rise <= 0) {
7615 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7616 goto error;
7617 }
7618
7619 if (srv->check.health)
7620 srv->check.health = srv->check.rise;
7621
7622 out:
7623 return err_code;
7624
7625 error:
7626 deinit_srv_agent_check(srv);
7627 err_code |= ERR_ALERT | ERR_FATAL;
7628 goto out;
7629 return 0;
7630}
7631
7632/* Parse the "fall" server keyword */
7633static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7634 char **errmsg)
7635{
7636 int err_code = 0;
7637
7638 if (!*args[*cur_arg + 1]) {
7639 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7640 goto error;
7641 }
7642
7643 srv->check.fall = atol(args[*cur_arg+1]);
7644 if (srv->check.fall <= 0) {
7645 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7646 goto error;
7647 }
7648
7649 out:
7650 return err_code;
7651
7652 error:
7653 deinit_srv_agent_check(srv);
7654 err_code |= ERR_ALERT | ERR_FATAL;
7655 goto out;
7656 return 0;
7657}
7658
7659/* Parse the "inter" server keyword */
7660static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7661 char **errmsg)
7662{
7663 const char *err = NULL;
7664 unsigned int delay;
7665 int err_code = 0;
7666
7667 if (!*(args[*cur_arg+1])) {
7668 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7669 goto error;
7670 }
7671
7672 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7673 if (err == PARSE_TIME_OVER) {
7674 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7675 args[*cur_arg+1], args[*cur_arg], srv->id);
7676 goto error;
7677 }
7678 else if (err == PARSE_TIME_UNDER) {
7679 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7680 args[*cur_arg+1], args[*cur_arg], srv->id);
7681 goto error;
7682 }
7683 else if (err) {
7684 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7685 *err, srv->id);
7686 goto error;
7687 }
7688 if (delay <= 0) {
7689 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7690 delay, args[*cur_arg], srv->id);
7691 goto error;
7692 }
7693 srv->check.inter = delay;
7694
7695 out:
7696 return err_code;
7697
7698 error:
7699 err_code |= ERR_ALERT | ERR_FATAL;
7700 goto out;
7701}
7702
7703
7704/* Parse the "fastinter" server keyword */
7705static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7706 char **errmsg)
7707{
7708 const char *err = NULL;
7709 unsigned int delay;
7710 int err_code = 0;
7711
7712 if (!*(args[*cur_arg+1])) {
7713 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7714 goto error;
7715 }
7716
7717 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7718 if (err == PARSE_TIME_OVER) {
7719 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7720 args[*cur_arg+1], args[*cur_arg], srv->id);
7721 goto error;
7722 }
7723 else if (err == PARSE_TIME_UNDER) {
7724 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7725 args[*cur_arg+1], args[*cur_arg], srv->id);
7726 goto error;
7727 }
7728 else if (err) {
7729 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7730 *err, srv->id);
7731 goto error;
7732 }
7733 if (delay <= 0) {
7734 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7735 delay, args[*cur_arg], srv->id);
7736 goto error;
7737 }
7738 srv->check.fastinter = delay;
7739
7740 out:
7741 return err_code;
7742
7743 error:
7744 err_code |= ERR_ALERT | ERR_FATAL;
7745 goto out;
7746}
7747
7748
7749/* Parse the "downinter" server keyword */
7750static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7751 char **errmsg)
7752{
7753 const char *err = NULL;
7754 unsigned int delay;
7755 int err_code = 0;
7756
7757 if (!*(args[*cur_arg+1])) {
7758 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7759 goto error;
7760 }
7761
7762 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7763 if (err == PARSE_TIME_OVER) {
7764 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7765 args[*cur_arg+1], args[*cur_arg], srv->id);
7766 goto error;
7767 }
7768 else if (err == PARSE_TIME_UNDER) {
7769 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7770 args[*cur_arg+1], args[*cur_arg], srv->id);
7771 goto error;
7772 }
7773 else if (err) {
7774 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7775 *err, srv->id);
7776 goto error;
7777 }
7778 if (delay <= 0) {
7779 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7780 delay, args[*cur_arg], srv->id);
7781 goto error;
7782 }
7783 srv->check.downinter = delay;
7784
7785 out:
7786 return err_code;
7787
7788 error:
7789 err_code |= ERR_ALERT | ERR_FATAL;
7790 goto out;
7791}
7792
7793/* Parse the "port" server keyword */
7794static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7795 char **errmsg)
7796{
7797 int err_code = 0;
7798
7799 if (!*(args[*cur_arg+1])) {
7800 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7801 goto error;
7802 }
7803
7804 global.maxsock++;
7805 srv->check.port = atol(args[*cur_arg+1]);
7806 srv->flags |= SRV_F_CHECKPORT;
7807
7808 out:
7809 return err_code;
7810
7811 error:
7812 err_code |= ERR_ALERT | ERR_FATAL;
7813 goto out;
7814}
7815
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007816static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007817 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7818 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7819 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007820 { 0, NULL, NULL },
7821}};
7822
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007823static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007824 { "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 +02007825 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7826 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7827 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7828 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7829 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007830 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007831 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007832 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7833 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007834 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007835 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7836 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7837 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7838 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7839 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7840 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7841 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7842 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007843 { NULL, NULL, 0 },
7844}};
7845
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007846INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007847INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007848
Willy Tarreaubd741542010-03-16 18:46:54 +01007849/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007850 * Local variables:
7851 * c-indent-level: 8
7852 * c-basic-offset: 8
7853 * End:
7854 */