blob: d3283509481bdcd1b4cc11e1b48687fbf1baffbe [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 Tarreau7cd8b6e2020-06-02 17:32:26 +020041#include <haproxy/regex.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020042#include <haproxy/tools.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020043#include <haproxy/time.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020044#include <haproxy/thread.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020045#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020046#include <haproxy/http_htx.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020047#include <haproxy/h1.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020048#include <haproxy/htx.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020049#include <haproxy/log.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020050#include <haproxy/proxy.h>
Willy Tarreau3727a8a2020-06-04 17:37:26 +020051#include <haproxy/signal.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020052#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020053#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020054#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020055#include <haproxy/task.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020056#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020057
Willy Tarreauf268ee82020-06-04 17:05:57 +020058#include <haproxy/global.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020059
Willy Tarreauaa74c4e2020-06-04 10:19:23 +020060#include <haproxy/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020061#include <proto/backend.h>
Willy Tarreau0f6ffd62020-06-03 19:33:00 +020062#include <haproxy/fd.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020063#include <proto/queue.h>
Willy Tarreaufc8f6a82020-06-03 19:20:59 +020064#include <haproxy/port_range.h>
Willy Tarreaufc774542020-06-04 17:31:04 +020065#include <haproxy/proto_tcp.h>
Willy Tarreau2dd7c352020-06-03 15:26:55 +020066#include <haproxy/protocol.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020067#include <proto/server.h>
Willy Tarreau832ce652020-06-04 08:36:05 +020068#include <haproxy/proto_udp.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020069#include <haproxy/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020070
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020071static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010072
Christopher Faulet61cc8522020-04-20 14:54:42 +020073static int wake_srv_chk(struct conn_stream *cs);
74struct data_cb check_conn_cb = {
75 .wake = wake_srv_chk,
76 .name = "CHCK",
77};
Christopher Fauletd7e63962020-04-17 20:15:59 +020078
Christopher Fauletd7cee712020-04-21 13:45:00 +020079/* Global tree to share all tcp-checks */
80struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020081
82
Willy Tarreau8ceae722018-11-26 11:58:30 +010083DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
84DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020085
Gaetan Rivet05d692d2020-02-14 17:42:54 +010086/* Dummy frontend used to create all checks sessions. */
87static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020088
Christopher Faulet61cc8522020-04-20 14:54:42 +020089/**************************************************************************/
90/************************ Handle check results ****************************/
91/**************************************************************************/
92struct check_status {
93 short result; /* one of SRV_CHK_* */
94 char *info; /* human readable short info */
95 char *desc; /* long description */
96};
97
98struct analyze_status {
99 char *desc; /* description */
100 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
101};
102
Simon Horman63a4a822012-03-19 07:24:41 +0900103static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100104 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
105 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200106 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200107
Willy Tarreau23964182014-05-20 20:56:30 +0200108 /* Below we have finished checks */
109 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100110 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100111
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100112 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200113
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100114 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
115 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
116 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200117
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100118 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
119 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
120 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200121
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100122 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
123 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200124
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200125 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200126
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100127 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
128 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
129 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900130
131 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
132 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200133 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200134};
135
Simon Horman63a4a822012-03-19 07:24:41 +0900136static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100137 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
138
139 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
140 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
141
142 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
143 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
144 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
145 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
146
147 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
148 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
149 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
150};
151
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100152/* checks if <err> is a real error for errno or one that can be ignored, and
153 * return 0 for these ones or <err> for real ones.
154 */
155static inline int unclean_errno(int err)
156{
157 if (err == EAGAIN || err == EINPROGRESS ||
158 err == EISCONN || err == EALREADY)
159 return 0;
160 return err;
161}
162
Christopher Faulet61cc8522020-04-20 14:54:42 +0200163/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200164const char *get_check_status_description(short check_status) {
165
166 const char *desc;
167
168 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200169 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200170 else
171 desc = NULL;
172
173 if (desc && *desc)
174 return desc;
175 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200176 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200177}
178
Christopher Faulet61cc8522020-04-20 14:54:42 +0200179/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200180const char *get_check_status_info(short check_status) {
181
182 const char *info;
183
184 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200185 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200186 else
187 info = NULL;
188
189 if (info && *info)
190 return info;
191 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200192 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200193}
194
Christopher Faulet61cc8522020-04-20 14:54:42 +0200195/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100196const char *get_analyze_status(short analyze_status) {
197
198 const char *desc;
199
200 if (analyze_status < HANA_STATUS_SIZE)
201 desc = analyze_statuses[analyze_status].desc;
202 else
203 desc = NULL;
204
205 if (desc && *desc)
206 return desc;
207 else
208 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
209}
210
Christopher Faulet61cc8522020-04-20 14:54:42 +0200211/* Sets check->status, update check->duration and fill check->result with an
212 * adequate CHK_RES_* value. The new check->health is computed based on the
213 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200214 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200215 * Shows information in logs about failed health check if server is UP or
216 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200217 */
Simon Horman4a741432013-02-23 15:35:38 +0900218static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100219{
Simon Horman4a741432013-02-23 15:35:38 +0900220 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200221 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200222 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900223
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200224 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100225 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900226 check->desc[0] = '\0';
227 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200228 return;
229 }
230
Simon Horman4a741432013-02-23 15:35:38 +0900231 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200232 return;
233
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200234 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900235 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
236 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200237 } else
Simon Horman4a741432013-02-23 15:35:38 +0900238 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200239
Simon Horman4a741432013-02-23 15:35:38 +0900240 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200241 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900242 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200243
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100244 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900245 check->duration = -1;
246 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200247 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900248 check->duration = tv_ms_elapsed(&check->start, &now);
249 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200250 }
251
Willy Tarreau23964182014-05-20 20:56:30 +0200252 /* no change is expected if no state change occurred */
253 if (check->result == CHK_RES_NEUTRAL)
254 return;
255
Olivier Houchard0923fa42019-01-11 18:43:04 +0100256 /* If the check was really just sending a mail, it won't have an
257 * associated server, so we're done now.
258 */
259 if (!s)
260 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200261 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200262
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200263 switch (check->result) {
264 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200265 /* Failure to connect to the agent as a secondary check should not
266 * cause the server to be marked down.
267 */
268 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900269 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200270 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100271 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200272 report = 1;
273 check->health--;
274 if (check->health < check->rise)
275 check->health = 0;
276 }
277 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200278
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200279 case CHK_RES_PASSED:
280 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
281 if ((check->health < check->rise + check->fall - 1) &&
282 (check->result == CHK_RES_PASSED || check->health > 0)) {
283 report = 1;
284 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200285
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200286 if (check->health >= check->rise)
287 check->health = check->rise + check->fall - 1; /* OK now */
288 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200289
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200290 /* clear consecutive_errors if observing is enabled */
291 if (s->onerror)
292 s->consecutive_errors = 0;
293 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100294
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200295 default:
296 break;
297 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200298
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200299 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
300 (status != prev_status || report)) {
301 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200302 "%s check for %sserver %s/%s %s%s",
303 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200304 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100305 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100306 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200307 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200308
Emeric Brun5a133512017-10-19 14:42:30 +0200309 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200310
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100311 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200312 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
313 (check->health >= check->rise) ? check->fall : check->rise,
314 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200315
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200316 ha_warning("%s.\n", trash.area);
317 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
318 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200319 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200320}
321
Willy Tarreau4eec5472014-05-20 22:32:27 +0200322/* Marks the check <check>'s server down if the current check is already failed
323 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200324 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200325static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200326{
Simon Horman4a741432013-02-23 15:35:38 +0900327 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900328
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200329 /* The agent secondary check should only cause a server to be marked
330 * as down if check->status is HCHK_STATUS_L7STS, which indicates
331 * that the agent returned "fail", "stopped" or "down".
332 * The implication here is that failure to connect to the agent
333 * as a secondary check should not cause the server to be marked
334 * down. */
335 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
336 return;
337
Willy Tarreau4eec5472014-05-20 22:32:27 +0200338 if (check->health > 0)
339 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100340
Willy Tarreau4eec5472014-05-20 22:32:27 +0200341 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200342 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200343}
344
Willy Tarreauaf549582014-05-16 17:37:50 +0200345/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200346 * it isn't in maintenance, it is not tracking a down server and other checks
347 * comply. The rule is simple : by default, a server is up, unless any of the
348 * following conditions is true :
349 * - health check failed (check->health < rise)
350 * - agent check failed (agent->health < rise)
351 * - the server tracks a down server (track && track->state == STOPPED)
352 * Note that if the server has a slowstart, it will switch to STARTING instead
353 * of RUNNING. Also, only the health checks support the nolb mode, so the
354 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200355 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200356static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200357{
Simon Horman4a741432013-02-23 15:35:38 +0900358 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100359
Emeric Brun52a91d32017-08-31 14:41:55 +0200360 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200361 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100362
Emeric Brun52a91d32017-08-31 14:41:55 +0200363 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200364 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100365
Willy Tarreau3e048382014-05-21 10:30:54 +0200366 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
367 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368
Willy Tarreau3e048382014-05-21 10:30:54 +0200369 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
370 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200371
Emeric Brun52a91d32017-08-31 14:41:55 +0200372 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200373 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100374
Emeric Brun5a133512017-10-19 14:42:30 +0200375 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100376}
377
Willy Tarreaudb58b792014-05-21 13:57:23 +0200378/* Marks the check <check> as valid and tries to set its server into stopping mode
379 * if it was running or starting, and provided it isn't in maintenance and other
380 * checks comply. The conditions for the server to be marked in stopping mode are
381 * the same as for it to be turned up. Also, only the health checks support the
382 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200383 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200384static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200385{
Simon Horman4a741432013-02-23 15:35:38 +0900386 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100387
Emeric Brun52a91d32017-08-31 14:41:55 +0200388 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200389 return;
390
Willy Tarreaudb58b792014-05-21 13:57:23 +0200391 if (check->state & CHK_ST_AGENT)
392 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100393
Emeric Brun52a91d32017-08-31 14:41:55 +0200394 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200395 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396
Willy Tarreaudb58b792014-05-21 13:57:23 +0200397 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
398 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100399
Willy Tarreaudb58b792014-05-21 13:57:23 +0200400 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
401 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100402
Willy Tarreaub26881a2017-12-23 11:16:49 +0100403 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100404}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200405
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100406/* note: use health_adjust() only, which first checks that the observe mode is
407 * enabled.
408 */
409void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100410{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100411 int failed;
412 int expire;
413
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100414 if (s->observe >= HANA_OBS_SIZE)
415 return;
416
Willy Tarreaubb956662013-01-24 00:37:39 +0100417 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100418 return;
419
420 switch (analyze_statuses[status].lr[s->observe - 1]) {
421 case 1:
422 failed = 1;
423 break;
424
425 case 2:
426 failed = 0;
427 break;
428
429 default:
430 return;
431 }
432
433 if (!failed) {
434 /* good: clear consecutive_errors */
435 s->consecutive_errors = 0;
436 return;
437 }
438
Olivier Houchard7059c552019-03-08 18:49:32 +0100439 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100440
441 if (s->consecutive_errors < s->consecutive_errors_limit)
442 return;
443
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100444 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
445 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100446
447 switch (s->onerror) {
448 case HANA_ONERR_FASTINTER:
449 /* force fastinter - nothing to do here as all modes force it */
450 break;
451
452 case HANA_ONERR_SUDDTH:
453 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900454 if (s->check.health > s->check.rise)
455 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100456
457 /* no break - fall through */
458
459 case HANA_ONERR_FAILCHK:
460 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200461 set_server_check_status(&s->check, HCHK_STATUS_HANA,
462 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200463 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100464 break;
465
466 case HANA_ONERR_MARKDWN:
467 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900468 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200469 set_server_check_status(&s->check, HCHK_STATUS_HANA,
470 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200471 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100472 break;
473
474 default:
475 /* write a warning? */
476 break;
477 }
478
479 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100480 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100481
Simon Horman66183002013-02-23 10:16:43 +0900482 if (s->check.fastinter) {
483 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300484 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200485 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300486 /* requeue check task with new expire */
487 task_queue(s->check.task);
488 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100489 }
Willy Tarreauef781042010-01-27 11:53:01 +0100490}
491
Christopher Faulet61cc8522020-04-20 14:54:42 +0200492/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100493 * closed, keep errno intact as it is supposed to contain the valid error code.
494 * If no error is reported, check the socket's error queue using getsockopt().
495 * Warning, this must be done only once when returning from poll, and never
496 * after an I/O error was attempted, otherwise the error queue might contain
497 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
498 * socket. Returns non-zero if an error was reported, zero if everything is
499 * clean (including a properly closed socket).
500 */
501static int retrieve_errno_from_socket(struct connection *conn)
502{
503 int skerr;
504 socklen_t lskerr = sizeof(skerr);
505
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100506 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100507 return 1;
508
Willy Tarreau3c728722014-01-23 13:50:42 +0100509 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100510 return 0;
511
Willy Tarreau585744b2017-08-24 14:31:19 +0200512 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100513 errno = skerr;
514
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100515 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100516
517 if (!errno) {
518 /* we could not retrieve an error, that does not mean there is
519 * none. Just don't change anything and only report the prior
520 * error if any.
521 */
522 if (conn->flags & CO_FL_ERROR)
523 return 1;
524 else
525 return 0;
526 }
527
528 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
529 return 1;
530}
531
Christopher Faulet61cc8522020-04-20 14:54:42 +0200532/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100533 * and adjust the server status accordingly. It may make use of <errno_bck>
534 * if non-null when the caller is absolutely certain of its validity (eg:
535 * checked just after a syscall). If the caller doesn't have a valid errno,
536 * it can pass zero, and retrieve_errno_from_socket() will be called to try
537 * to extract errno from the socket. If no error is reported, it will consider
538 * the <expired> flag. This is intended to be used when a connection error was
539 * reported in conn->flags or when a timeout was reported in <expired>. The
540 * function takes care of not updating a server status which was already set.
541 * All situations where at least one of <expired> or CO_FL_ERROR are set
542 * produce a status.
543 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200544static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100545{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200546 struct conn_stream *cs = check->cs;
547 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200549 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200550 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100551
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100552 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100553 return;
554
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100555 errno = unclean_errno(errno_bck);
556 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100557 retrieve_errno_from_socket(conn);
558
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200559 if (conn && !(conn->flags & CO_FL_ERROR) &&
560 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100561 return;
562
563 /* we'll try to build a meaningful error message depending on the
564 * context of the error possibly present in conn->err_code, and the
565 * socket error possibly collected above. This is useful to know the
566 * exact step of the L6 layer (eg: SSL handshake).
567 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200568 chk = get_trash_chunk();
569
Christopher Faulet799f3a42020-04-07 12:06:14 +0200570 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200571 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200572 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200573 if (!step)
574 chunk_printf(chk, " at initial connection step of tcp-check");
575 else {
576 chunk_printf(chk, " at step %d of tcp-check", step);
577 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200578 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
579 if (check->current_step->connect.port)
580 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200581 else
582 chunk_appendf(chk, " (connect)");
583 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200584 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
585 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100586
587 switch (expect->type) {
588 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200589 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100590 break;
591 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200592 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100593 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200594 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200595 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100596 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200597 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100598 chunk_appendf(chk, " (expect binary regex)");
599 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200600 case TCPCHK_EXPECT_STRING_LF:
601 chunk_appendf(chk, " (expect log-format string)");
602 break;
603 case TCPCHK_EXPECT_BINARY_LF:
604 chunk_appendf(chk, " (expect log-format binary)");
605 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200606 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200607 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200608 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200609 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200610 chunk_appendf(chk, " (expect HTTP status regex)");
611 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200612 case TCPCHK_EXPECT_HTTP_HEADER:
613 chunk_appendf(chk, " (expect HTTP header pattern)");
614 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200615 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200616 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200617 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200618 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200619 chunk_appendf(chk, " (expect HTTP body regex)");
620 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200621 case TCPCHK_EXPECT_HTTP_BODY_LF:
622 chunk_appendf(chk, " (expect log-format HTTP body)");
623 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200624 case TCPCHK_EXPECT_CUSTOM:
625 chunk_appendf(chk, " (expect custom function)");
626 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100627 case TCPCHK_EXPECT_UNDEF:
628 chunk_appendf(chk, " (undefined expect!)");
629 break;
630 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200631 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200632 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200633 chunk_appendf(chk, " (send)");
634 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200635
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200636 if (check->current_step && check->current_step->comment)
637 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200638 }
639 }
640
Willy Tarreau00149122017-10-04 18:05:01 +0200641 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100642 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200643 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
644 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100645 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200646 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
647 chk->area);
648 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100649 }
650 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100651 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200652 chunk_printf(&trash, "%s%s", strerror(errno),
653 chk->area);
654 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100655 }
656 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200657 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100658 }
659 }
660
Willy Tarreau00149122017-10-04 18:05:01 +0200661 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200662 /* NOTE: this is reported after <fall> tries */
663 chunk_printf(chk, "No port available for the TCP connection");
664 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
665 }
666
Willy Tarreau00149122017-10-04 18:05:01 +0200667 if (!conn) {
668 /* connection allocation error before the connection was established */
669 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
670 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100671 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100672 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200673 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100674 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
675 else if (expired)
676 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200677
678 /*
679 * might be due to a server IP change.
680 * Let's trigger a DNS resolution if none are currently running.
681 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100682 if (check->server)
683 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200684
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100685 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100686 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100687 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200688 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100689 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
690 else if (expired)
691 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
692 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200693 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100694 /* I/O error after connection was established and before we could diagnose */
695 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
696 }
697 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200698 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
699
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100700 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200701 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
702 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200703 tout = check->current_step->expect.tout_status;
704 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100705 }
706
707 return;
708}
709
Willy Tarreaubaaee002006-06-26 02:48:02 +0200710
Christopher Faulet61cc8522020-04-20 14:54:42 +0200711/**************************************************************************/
712/*************** Init/deinit tcp-check rules and ruleset ******************/
713/**************************************************************************/
714/* Releases memory allocated for a log-format string */
715static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200716{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200717 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100718
Christopher Faulet61cc8522020-04-20 14:54:42 +0200719 list_for_each_entry_safe(lf, lfb, fmt, list) {
720 LIST_DEL(&lf->list);
721 release_sample_expr(lf->expr);
722 free(lf->arg);
723 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100724 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200725}
726
Christopher Faulet61cc8522020-04-20 14:54:42 +0200727/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
728static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100729{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200730 if (!hdr)
731 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200732
Christopher Faulet61cc8522020-04-20 14:54:42 +0200733 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200734 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200735 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100736}
737
Christopher Faulet61cc8522020-04-20 14:54:42 +0200738/* Releases memory allocated for an HTTP header list used in a tcp-check send
739 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200740 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200742{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200743 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200744
Christopher Faulet61cc8522020-04-20 14:54:42 +0200745 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
746 LIST_DEL(&hdr->list);
747 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200748 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200749}
750
Christopher Faulet61cc8522020-04-20 14:54:42 +0200751/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
752 * tcp-check was allocated using a memory pool (it is used to instantiate email
753 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200754 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200755static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200756{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200757 if (!rule)
758 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200759
Christopher Faulet61cc8522020-04-20 14:54:42 +0200760 free(rule->comment);
761 switch (rule->action) {
762 case TCPCHK_ACT_SEND:
763 switch (rule->send.type) {
764 case TCPCHK_SEND_STRING:
765 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200766 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200767 break;
768 case TCPCHK_SEND_STRING_LF:
769 case TCPCHK_SEND_BINARY_LF:
770 free_tcpcheck_fmt(&rule->send.fmt);
771 break;
772 case TCPCHK_SEND_HTTP:
773 free(rule->send.http.meth.str.area);
774 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200775 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200776 else
777 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200778 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200779 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
780 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200781 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200782 else
783 free_tcpcheck_fmt(&rule->send.http.body_fmt);
784 break;
785 case TCPCHK_SEND_UNDEF:
786 break;
787 }
788 break;
789 case TCPCHK_ACT_EXPECT:
790 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
791 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
792 release_sample_expr(rule->expect.status_expr);
793 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200794 case TCPCHK_EXPECT_HTTP_STATUS:
795 free(rule->expect.codes.codes);
796 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200797 case TCPCHK_EXPECT_STRING:
798 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200799 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200800 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200801 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200802 case TCPCHK_EXPECT_STRING_REGEX:
803 case TCPCHK_EXPECT_BINARY_REGEX:
804 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
805 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200806 regex_free(rule->expect.regex);
807 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200808 case TCPCHK_EXPECT_STRING_LF:
809 case TCPCHK_EXPECT_BINARY_LF:
810 case TCPCHK_EXPECT_HTTP_BODY_LF:
811 free_tcpcheck_fmt(&rule->expect.fmt);
812 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200813 case TCPCHK_EXPECT_HTTP_HEADER:
814 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
815 regex_free(rule->expect.hdr.name_re);
816 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
817 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
818 else
819 istfree(&rule->expect.hdr.name);
820
821 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
822 regex_free(rule->expect.hdr.value_re);
823 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
824 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
825 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
826 istfree(&rule->expect.hdr.value);
827 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200828 case TCPCHK_EXPECT_CUSTOM:
829 case TCPCHK_EXPECT_UNDEF:
830 break;
831 }
832 break;
833 case TCPCHK_ACT_CONNECT:
834 free(rule->connect.sni);
835 free(rule->connect.alpn);
836 release_sample_expr(rule->connect.port_expr);
837 break;
838 case TCPCHK_ACT_COMMENT:
839 break;
840 case TCPCHK_ACT_ACTION_KW:
841 free(rule->action_kw.rule);
842 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200843 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200844
845 if (in_pool)
846 pool_free(pool_head_tcpcheck_rule, rule);
847 else
848 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200849}
850
Christopher Faulet61cc8522020-04-20 14:54:42 +0200851/* Creates a tcp-check variable used in preset variables before executing a
852 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100853 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200854static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100855{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200856 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100857
Christopher Faulet61cc8522020-04-20 14:54:42 +0200858 var = calloc(1, sizeof(*var));
859 if (var == NULL)
860 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100861
Christopher Fauletb61caf42020-04-21 10:57:42 +0200862 var->name = istdup(name);
863 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200864 free(var);
865 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100866 }
Simon Horman98637e52014-06-20 12:30:16 +0900867
Christopher Faulet61cc8522020-04-20 14:54:42 +0200868 LIST_INIT(&var->list);
869 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900870}
871
Christopher Faulet61cc8522020-04-20 14:54:42 +0200872/* Releases memory allocated for a preset tcp-check variable */
873static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900874{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200875 if (!var)
876 return;
877
Christopher Fauletb61caf42020-04-21 10:57:42 +0200878 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200879 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
880 free(var->data.u.str.area);
881 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
882 free(var->data.u.meth.str.area);
883 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900884}
885
Christopher Faulet61cc8522020-04-20 14:54:42 +0200886/* Releases a list of preset tcp-check variables */
887static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900888{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200889 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200890
Christopher Faulet61cc8522020-04-20 14:54:42 +0200891 list_for_each_entry_safe(var, back, vars, list) {
892 LIST_DEL(&var->list);
893 free_tcpcheck_var(var);
894 }
Simon Horman98637e52014-06-20 12:30:16 +0900895}
896
Christopher Faulet61cc8522020-04-20 14:54:42 +0200897/* Duplicate a list of preset tcp-check variables */
898int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900899{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200900 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900901
Christopher Faulet61cc8522020-04-20 14:54:42 +0200902 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200903 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200904 if (!new)
905 goto error;
906 new->data.type = var->data.type;
907 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
908 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
909 goto error;
910 if (var->data.type == SMP_T_STR)
911 new->data.u.str.area[new->data.u.str.data] = 0;
912 }
913 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
914 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
915 goto error;
916 new->data.u.str.area[new->data.u.str.data] = 0;
917 new->data.u.meth.meth = var->data.u.meth.meth;
918 }
919 else
920 new->data.u = var->data.u;
921 LIST_ADDQ(dst, &new->list);
922 }
923 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900924
Christopher Faulet61cc8522020-04-20 14:54:42 +0200925 error:
926 free(new);
927 return 0;
928}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200929
Christopher Faulet61cc8522020-04-20 14:54:42 +0200930/* Looks for a shared tcp-check ruleset given its name. */
931static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
932{
933 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200934 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900935
Christopher Fauletd7cee712020-04-21 13:45:00 +0200936 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
937 if (node) {
938 rs = container_of(node, typeof(*rs), node);
939 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200940 }
941 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900942}
943
Christopher Faulet56192cc2020-05-29 08:10:50 +0200944/* Creates a new shared tcp-check ruleset and insert it in shared_tcpchecks
945 * tree.
946 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200947static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900948{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200949 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900950
Christopher Faulet61cc8522020-04-20 14:54:42 +0200951 rs = calloc(1, sizeof(*rs));
952 if (rs == NULL)
953 return NULL;
954
Christopher Fauletd7cee712020-04-21 13:45:00 +0200955 rs->node.key = strdup(name);
956 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200957 free(rs);
958 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900959 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200960
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200962 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200963 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900964}
965
Christopher Faulet61cc8522020-04-20 14:54:42 +0200966/* Releases memory allocated by a tcp-check ruleset. */
967static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900968{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200969 struct tcpcheck_rule *r, *rb;
970 if (!rs)
971 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200972
Christopher Fauletd7cee712020-04-21 13:45:00 +0200973 ebpt_delete(&rs->node);
974 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200975 list_for_each_entry_safe(r, rb, &rs->rules, list) {
976 LIST_DEL(&r->list);
977 free_tcpcheck(r, 0);
978 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200979 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900980}
981
Christopher Faulet61cc8522020-04-20 14:54:42 +0200982
983/**************************************************************************/
984/**************** Everything about tcp-checks execution *******************/
985/**************************************************************************/
986/* Returns the id of a step in a tcp-check ruleset */
987static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200988{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200989 if (!rule)
990 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900991
Christopher Faulet61cc8522020-04-20 14:54:42 +0200992 /* no last started step => first step */
993 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900994 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900995
Christopher Faulet61cc8522020-04-20 14:54:42 +0200996 /* last step is the first implicit connect */
997 if (rule->index == 0 &&
998 rule->action == TCPCHK_ACT_CONNECT &&
999 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1000 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001001
Christopher Faulet61cc8522020-04-20 14:54:42 +02001002 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001003}
1004
Christopher Faulet61cc8522020-04-20 14:54:42 +02001005/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1006 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001007 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001008static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001009{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001010 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001011
Christopher Faulet61cc8522020-04-20 14:54:42 +02001012 list_for_each_entry(r, rules->list, list) {
1013 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1014 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001015 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001016 return NULL;
1017}
Cyril Bontéac92a062014-12-27 22:28:38 +01001018
Christopher Faulet61cc8522020-04-20 14:54:42 +02001019/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1020 * NULL if none was found.
1021 */
1022static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1023{
1024 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001025
Christopher Faulet61cc8522020-04-20 14:54:42 +02001026 list_for_each_entry_rev(r, rules->list, list) {
1027 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1028 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001029 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001030 return NULL;
1031}
Cyril Bontéac92a062014-12-27 22:28:38 +01001032
Christopher Faulet61cc8522020-04-20 14:54:42 +02001033/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1034 * <start> or NULL if non was found. If <start> is NULL, it relies on
1035 * get_first_tcpcheck_rule().
1036 */
1037static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1038{
1039 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001040
Christopher Faulet61cc8522020-04-20 14:54:42 +02001041 if (!start)
1042 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001043
Christopher Faulet61cc8522020-04-20 14:54:42 +02001044 r = LIST_NEXT(&start->list, typeof(r), list);
1045 list_for_each_entry_from(r, rules->list, list) {
1046 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1047 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001048 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001049 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001050}
Simon Horman98637e52014-06-20 12:30:16 +09001051
Simon Horman98637e52014-06-20 12:30:16 +09001052
Christopher Faulet61cc8522020-04-20 14:54:42 +02001053/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1054static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1055 int match, struct ist info)
1056{
1057 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001058
Christopher Faulet61cc8522020-04-20 14:54:42 +02001059 /* Follows these step to produce the info message:
1060 * 1. if info field is already provided, copy it
1061 * 2. if the expect rule provides an onerror log-format string,
1062 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001063 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001064 * 4. Otherwise produce the generic tcp-check info message
1065 */
1066 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001067 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001068 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001069 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001070 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1071 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1072 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001073 }
Simon Horman98637e52014-06-20 12:30:16 +09001074
Christopher Faulet61cc8522020-04-20 14:54:42 +02001075 if (check->type == PR_O2_TCPCHK_CHK &&
1076 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1077 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001078
Christopher Faulet61cc8522020-04-20 14:54:42 +02001079 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1080 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001081 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001082 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1083 break;
1084 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001085 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001086 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001087 tcpcheck_get_step_id(check, rule));
1088 break;
1089 case TCPCHK_EXPECT_BINARY:
1090 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1091 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001092 case TCPCHK_EXPECT_STRING_REGEX:
1093 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1094 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001095 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1096 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001097 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001098 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001100 case TCPCHK_EXPECT_STRING_LF:
1101 case TCPCHK_EXPECT_HTTP_BODY_LF:
1102 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1103 break;
1104 case TCPCHK_EXPECT_BINARY_LF:
1105 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1106 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001107 case TCPCHK_EXPECT_CUSTOM:
1108 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1109 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001110 case TCPCHK_EXPECT_HTTP_HEADER:
1111 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001112 case TCPCHK_EXPECT_UNDEF:
1113 /* Should never happen. */
1114 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001115 }
1116
Christopher Faulet61cc8522020-04-20 14:54:42 +02001117 comment:
1118 /* If the failing expect rule provides a comment, it is concatenated to
1119 * the info message.
1120 */
1121 if (rule->comment) {
1122 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001123 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001124 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001125
Christopher Faulet61cc8522020-04-20 14:54:42 +02001126 /* Finally, the check status code is set if the failing expect rule
1127 * defines a status expression.
1128 */
1129 if (rule->expect.status_expr) {
1130 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001131 rule->expect.status_expr, SMP_T_STR);
1132
1133 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1134 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001135 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001136 }
Simon Horman98637e52014-06-20 12:30:16 +09001137
Christopher Faulet61cc8522020-04-20 14:54:42 +02001138 *(b_tail(msg)) = '\0';
1139}
Cyril Bontéac92a062014-12-27 22:28:38 +01001140
Christopher Faulet61cc8522020-04-20 14:54:42 +02001141/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1142static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1143 struct ist info)
1144{
1145 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001146
Christopher Faulet61cc8522020-04-20 14:54:42 +02001147 /* Follows these step to produce the info message:
1148 * 1. if info field is already provided, copy it
1149 * 2. if the expect rule provides an onsucces log-format string,
1150 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001151 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001152 * 4. Otherwise produce the generic tcp-check info message
1153 */
1154 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001155 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001156 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1157 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1158 &rule->expect.onsuccess_fmt);
1159 else if (check->type == PR_O2_TCPCHK_CHK &&
1160 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1161 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001162
Christopher Faulet61cc8522020-04-20 14:54:42 +02001163 /* Finally, the check status code is set if the expect rule defines a
1164 * status expression.
1165 */
1166 if (rule->expect.status_expr) {
1167 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001168 rule->expect.status_expr, SMP_T_STR);
1169
1170 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1171 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001172 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001173 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001174
1175 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001176}
1177
Christopher Faulet61cc8522020-04-20 14:54:42 +02001178/* Builds the server state header used by HTTP health-checks */
1179static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001180{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001181 int sv_state;
1182 int ratio;
1183 char addr[46];
1184 char port[6];
1185 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1186 "UP %d/%d", "UP",
1187 "NOLB %d/%d", "NOLB",
1188 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001189
Christopher Faulet61cc8522020-04-20 14:54:42 +02001190 if (!(s->check.state & CHK_ST_ENABLED))
1191 sv_state = 6;
1192 else if (s->cur_state != SRV_ST_STOPPED) {
1193 if (s->check.health == s->check.rise + s->check.fall - 1)
1194 sv_state = 3; /* UP */
1195 else
1196 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001197
Christopher Faulet61cc8522020-04-20 14:54:42 +02001198 if (s->cur_state == SRV_ST_STOPPING)
1199 sv_state += 2;
1200 } else {
1201 if (s->check.health)
1202 sv_state = 1; /* going up */
1203 else
1204 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001205 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001206
Christopher Faulet61cc8522020-04-20 14:54:42 +02001207 chunk_appendf(buf, srv_hlt_st[sv_state],
1208 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1209 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001210
Christopher Faulet61cc8522020-04-20 14:54:42 +02001211 addr_to_str(&s->addr, addr, sizeof(addr));
1212 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1213 snprintf(port, sizeof(port), "%u", s->svc_port);
1214 else
1215 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001216
Christopher Faulet61cc8522020-04-20 14:54:42 +02001217 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1218 addr, port, s->proxy->id, s->id,
1219 global.node,
1220 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1221 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1222 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1223 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001224
Christopher Faulet61cc8522020-04-20 14:54:42 +02001225 if ((s->cur_state == SRV_ST_STARTING) &&
1226 now.tv_sec < s->last_change + s->slowstart &&
1227 now.tv_sec >= s->last_change) {
1228 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1229 chunk_appendf(buf, "; throttle=%d%%", ratio);
1230 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001231
Christopher Faulet61cc8522020-04-20 14:54:42 +02001232 return b_data(buf);
1233}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001234
Christopher Faulet61cc8522020-04-20 14:54:42 +02001235/* Internal functions to parse and validate a MySQL packet in the context of an
1236 * expect rule. It start to parse the input buffer at the offset <offset>. If
1237 * <last_read> is set, no more data are expected.
1238 */
1239static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1240 unsigned int offset, int last_read)
1241{
1242 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1243 enum healthcheck_status status;
1244 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001245 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001246 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001247
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001248
Christopher Faulet61cc8522020-04-20 14:54:42 +02001249 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001250 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001251 if (!last_read)
1252 goto wait_more_data;
1253
1254 /* invalid length or truncated response */
1255 status = HCHK_STATUS_L7RSP;
1256 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001257 }
1258
Christopher Faulet61cc8522020-04-20 14:54:42 +02001259 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1260 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1261 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001262
Christopher Faulet61cc8522020-04-20 14:54:42 +02001263 if (b_data(&check->bi) < offset+plen+4) {
1264 if (!last_read)
1265 goto wait_more_data;
1266
1267 /* invalid length or truncated response */
1268 status = HCHK_STATUS_L7RSP;
1269 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001270 }
Simon Horman98637e52014-06-20 12:30:16 +09001271
Christopher Faulet61cc8522020-04-20 14:54:42 +02001272 if (*b_peek(&check->bi, offset+4) == '\xff') {
1273 /* MySQL Error packet always begin with field_count = 0xff */
1274 status = HCHK_STATUS_L7STS;
1275 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1276 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1277 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1278 goto error;
1279 }
Simon Horman98637e52014-06-20 12:30:16 +09001280
Christopher Faulet61cc8522020-04-20 14:54:42 +02001281 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1282 /* Not the last rule, continue */
1283 goto out;
1284 }
Simon Horman98637e52014-06-20 12:30:16 +09001285
Christopher Faulet61cc8522020-04-20 14:54:42 +02001286 /* We set the MySQL Version in description for information purpose
1287 * FIXME : it can be cool to use MySQL Version for other purpose,
1288 * like mark as down old MySQL server.
1289 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001290 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1291 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001292
Christopher Faulet61cc8522020-04-20 14:54:42 +02001293 out:
1294 free_trash_chunk(msg);
1295 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001296
Christopher Faulet61cc8522020-04-20 14:54:42 +02001297 error:
1298 ret = TCPCHK_EVAL_STOP;
1299 check->code = err;
1300 msg = alloc_trash_chunk();
1301 if (msg)
1302 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1303 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1304 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001305
Christopher Faulet61cc8522020-04-20 14:54:42 +02001306 wait_more_data:
1307 ret = TCPCHK_EVAL_WAIT;
1308 goto out;
1309}
Simon Horman98637e52014-06-20 12:30:16 +09001310
Christopher Faulet61cc8522020-04-20 14:54:42 +02001311/* Custom tcp-check expect function to parse and validate the MySQL initial
1312 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1313 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1314 * error occurred.
1315 */
1316static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1317{
1318 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1319}
Simon Horman98637e52014-06-20 12:30:16 +09001320
Christopher Faulet61cc8522020-04-20 14:54:42 +02001321/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1322 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1323 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1324 * an error occurred.
1325 */
1326static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1327{
1328 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001329
Christopher Faulet61cc8522020-04-20 14:54:42 +02001330 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1331 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1332 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001333
Christopher Faulet61cc8522020-04-20 14:54:42 +02001334 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1335}
Simon Horman98637e52014-06-20 12:30:16 +09001336
Christopher Faulet61cc8522020-04-20 14:54:42 +02001337/* Custom tcp-check expect function to parse and validate the LDAP bind response
1338 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1339 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1340 * error occurred.
1341 */
1342static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1343{
1344 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1345 enum healthcheck_status status;
1346 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001347 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001348 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001349
Christopher Faulet61cc8522020-04-20 14:54:42 +02001350 /* Check if the server speaks LDAP (ASN.1/BER)
1351 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1352 * http://tools.ietf.org/html/rfc4511
1353 */
1354 /* size of LDAPMessage */
1355 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001356
Christopher Faulet61cc8522020-04-20 14:54:42 +02001357 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1358 * messageID: 0x02 0x01 0x01: INTEGER 1
1359 * protocolOp: 0x61: bindResponse
1360 */
1361 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1362 status = HCHK_STATUS_L7RSP;
1363 desc = ist("Not LDAPv3 protocol");
1364 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001365 }
Simon Horman98637e52014-06-20 12:30:16 +09001366
Christopher Faulet61cc8522020-04-20 14:54:42 +02001367 /* size of bindResponse */
1368 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001369
Christopher Faulet61cc8522020-04-20 14:54:42 +02001370 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1371 * ldapResult: 0x0a 0x01: ENUMERATION
1372 */
1373 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1374 status = HCHK_STATUS_L7RSP;
1375 desc = ist("Not LDAPv3 protocol");
1376 goto error;
1377 }
Simon Horman98637e52014-06-20 12:30:16 +09001378
Christopher Faulet61cc8522020-04-20 14:54:42 +02001379 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1380 * resultCode
1381 */
1382 check->code = *(b_head(&check->bi) + msglen + 9);
1383 if (check->code) {
1384 status = HCHK_STATUS_L7STS;
1385 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1386 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001387 }
1388
Christopher Faulet1941bab2020-05-05 07:55:50 +02001389 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1390 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001391
Christopher Faulet61cc8522020-04-20 14:54:42 +02001392 out:
1393 free_trash_chunk(msg);
1394 return ret;
1395
1396 error:
1397 ret = TCPCHK_EVAL_STOP;
1398 msg = alloc_trash_chunk();
1399 if (msg)
1400 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1401 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1402 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001403}
1404
Christopher Faulet61cc8522020-04-20 14:54:42 +02001405/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1406 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1407 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001408 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001409static 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 +02001410{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001411 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1412 enum healthcheck_status status;
1413 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001414 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001415 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001416
Willy Tarreaubaaee002006-06-26 02:48:02 +02001417
Christopher Faulet61cc8522020-04-20 14:54:42 +02001418 memcpy(&framesz, b_head(&check->bi), 4);
1419 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001420
Christopher Faulet61cc8522020-04-20 14:54:42 +02001421 if (!last_read && b_data(&check->bi) < (4+framesz))
1422 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001423
Christopher Faulet61cc8522020-04-20 14:54:42 +02001424 memset(b_orig(&trash), 0, b_size(&trash));
1425 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1426 status = HCHK_STATUS_L7RSP;
1427 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1428 goto error;
1429 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001430
Christopher Faulet1941bab2020-05-05 07:55:50 +02001431 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1432 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001433
Christopher Faulet61cc8522020-04-20 14:54:42 +02001434 out:
1435 free_trash_chunk(msg);
1436 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001437
Christopher Faulet61cc8522020-04-20 14:54:42 +02001438 error:
1439 ret = TCPCHK_EVAL_STOP;
1440 msg = alloc_trash_chunk();
1441 if (msg)
1442 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1443 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1444 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001445
Christopher Faulet61cc8522020-04-20 14:54:42 +02001446 wait_more_data:
1447 ret = TCPCHK_EVAL_WAIT;
1448 goto out;
1449}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001450
Christopher Faulet61cc8522020-04-20 14:54:42 +02001451/* Custom tcp-check expect function to parse and validate the agent-check
1452 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1453 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1454 */
1455static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1456{
1457 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1458 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1459 const char *hs = NULL; /* health status */
1460 const char *as = NULL; /* admin status */
1461 const char *ps = NULL; /* performance status */
1462 const char *cs = NULL; /* maxconn */
1463 const char *err = NULL; /* first error to report */
1464 const char *wrn = NULL; /* first warning to report */
1465 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001466
Christopher Faulet61cc8522020-04-20 14:54:42 +02001467 /* We're getting an agent check response. The agent could
1468 * have been disabled in the mean time with a long check
1469 * still pending. It is important that we ignore the whole
1470 * response.
1471 */
1472 if (!(check->state & CHK_ST_ENABLED))
1473 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001474
Christopher Faulet61cc8522020-04-20 14:54:42 +02001475 /* The agent supports strings made of a single line ended by the
1476 * first CR ('\r') or LF ('\n'). This line is composed of words
1477 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1478 * line may optionally contained a description of a state change
1479 * after a sharp ('#'), which is only considered if a health state
1480 * is announced.
1481 *
1482 * Words may be composed of :
1483 * - a numeric weight suffixed by the percent character ('%').
1484 * - a health status among "up", "down", "stopped", and "fail".
1485 * - an admin status among "ready", "drain", "maint".
1486 *
1487 * These words may appear in any order. If multiple words of the
1488 * same category appear, the last one wins.
1489 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001490
Christopher Faulet61cc8522020-04-20 14:54:42 +02001491 p = b_head(&check->bi);
1492 while (*p && *p != '\n' && *p != '\r')
1493 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001494
Christopher Faulet61cc8522020-04-20 14:54:42 +02001495 if (!*p) {
1496 if (!last_read)
1497 goto wait_more_data;
1498
1499 /* at least inform the admin that the agent is mis-behaving */
1500 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1501 goto out;
1502 }
1503
1504 *p = 0;
1505 cmd = b_head(&check->bi);
1506
1507 while (*cmd) {
1508 /* look for next word */
1509 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1510 cmd++;
1511 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001512 }
1513
Christopher Faulet61cc8522020-04-20 14:54:42 +02001514 if (*cmd == '#') {
1515 /* this is the beginning of a health status description,
1516 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001517 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001518 cmd++;
1519 while (*cmd == '\t' || *cmd == ' ')
1520 cmd++;
1521 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001522 }
1523
Christopher Faulet61cc8522020-04-20 14:54:42 +02001524 /* find the end of the word so that we have a null-terminated
1525 * word between <cmd> and <p>.
1526 */
1527 p = cmd + 1;
1528 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1529 p++;
1530 if (*p)
1531 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001532
Christopher Faulet61cc8522020-04-20 14:54:42 +02001533 /* first, health statuses */
1534 if (strcasecmp(cmd, "up") == 0) {
1535 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1536 status = HCHK_STATUS_L7OKD;
1537 hs = cmd;
1538 }
1539 else if (strcasecmp(cmd, "down") == 0) {
1540 check->server->check.health = 0;
1541 status = HCHK_STATUS_L7STS;
1542 hs = cmd;
1543 }
1544 else if (strcasecmp(cmd, "stopped") == 0) {
1545 check->server->check.health = 0;
1546 status = HCHK_STATUS_L7STS;
1547 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001548 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001549 else if (strcasecmp(cmd, "fail") == 0) {
1550 check->server->check.health = 0;
1551 status = HCHK_STATUS_L7STS;
1552 hs = cmd;
1553 }
1554 /* admin statuses */
1555 else if (strcasecmp(cmd, "ready") == 0) {
1556 as = cmd;
1557 }
1558 else if (strcasecmp(cmd, "drain") == 0) {
1559 as = cmd;
1560 }
1561 else if (strcasecmp(cmd, "maint") == 0) {
1562 as = cmd;
1563 }
1564 /* try to parse a weight here and keep the last one */
1565 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1566 ps = cmd;
1567 }
1568 /* try to parse a maxconn here */
1569 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1570 cs = cmd;
1571 }
1572 else {
1573 /* keep a copy of the first error */
1574 if (!err)
1575 err = cmd;
1576 }
1577 /* skip to next word */
1578 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001579 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001580 /* here, cmd points either to \0 or to the beginning of a
1581 * description. Skip possible leading spaces.
1582 */
1583 while (*cmd == ' ' || *cmd == '\n')
1584 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001585
Christopher Faulet61cc8522020-04-20 14:54:42 +02001586 /* First, update the admin status so that we avoid sending other
1587 * possibly useless warnings and can also update the health if
1588 * present after going back up.
1589 */
1590 if (as) {
1591 if (strcasecmp(as, "drain") == 0)
1592 srv_adm_set_drain(check->server);
1593 else if (strcasecmp(as, "maint") == 0)
1594 srv_adm_set_maint(check->server);
1595 else
1596 srv_adm_set_ready(check->server);
1597 }
Simon Horman98637e52014-06-20 12:30:16 +09001598
Christopher Faulet61cc8522020-04-20 14:54:42 +02001599 /* now change weights */
1600 if (ps) {
1601 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001602
Christopher Faulet61cc8522020-04-20 14:54:42 +02001603 msg = server_parse_weight_change_request(check->server, ps);
1604 if (!wrn || !*wrn)
1605 wrn = msg;
1606 }
Simon Horman98637e52014-06-20 12:30:16 +09001607
Christopher Faulet61cc8522020-04-20 14:54:42 +02001608 if (cs) {
1609 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001610
Christopher Faulet61cc8522020-04-20 14:54:42 +02001611 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001612
Christopher Faulet61cc8522020-04-20 14:54:42 +02001613 msg = server_parse_maxconn_change_request(check->server, cs);
1614 if (!wrn || !*wrn)
1615 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001616 }
1617
Christopher Faulet61cc8522020-04-20 14:54:42 +02001618 /* and finally health status */
1619 if (hs) {
1620 /* We'll report some of the warnings and errors we have
1621 * here. Down reports are critical, we leave them untouched.
1622 * Lack of report, or report of 'UP' leaves the room for
1623 * ERR first, then WARN.
1624 */
1625 const char *msg = cmd;
1626 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001627
Christopher Faulet61cc8522020-04-20 14:54:42 +02001628 if (!*msg || status == HCHK_STATUS_L7OKD) {
1629 if (err && *err)
1630 msg = err;
1631 else if (wrn && *wrn)
1632 msg = wrn;
1633 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001634
Christopher Faulet61cc8522020-04-20 14:54:42 +02001635 t = get_trash_chunk();
1636 chunk_printf(t, "via agent : %s%s%s%s",
1637 hs, *msg ? " (" : "",
1638 msg, *msg ? ")" : "");
1639 set_server_check_status(check, status, t->area);
1640 }
1641 else if (err && *err) {
1642 /* No status change but we'd like to report something odd.
1643 * Just report the current state and copy the message.
1644 */
1645 chunk_printf(&trash, "agent reports an error : %s", err);
1646 set_server_check_status(check, status/*check->status*/, trash.area);
1647 }
1648 else if (wrn && *wrn) {
1649 /* No status change but we'd like to report something odd.
1650 * Just report the current state and copy the message.
1651 */
1652 chunk_printf(&trash, "agent warns : %s", wrn);
1653 set_server_check_status(check, status/*check->status*/, trash.area);
1654 }
1655 else
1656 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001657
Christopher Faulet61cc8522020-04-20 14:54:42 +02001658 out:
1659 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001660
Christopher Faulet61cc8522020-04-20 14:54:42 +02001661 wait_more_data:
1662 ret = TCPCHK_EVAL_WAIT;
1663 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001664}
1665
Christopher Faulet61cc8522020-04-20 14:54:42 +02001666/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1667 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1668 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001669 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001670static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001671{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001672 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1673 struct tcpcheck_connect *connect = &rule->connect;
1674 struct proxy *proxy = check->proxy;
1675 struct server *s = check->server;
1676 struct task *t = check->task;
1677 struct conn_stream *cs;
1678 struct connection *conn = NULL;
1679 struct protocol *proto;
1680 struct xprt_ops *xprt;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001681 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001682 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001683
Christopher Faulet61cc8522020-04-20 14:54:42 +02001684 /* For a connect action we'll create a new connection. We may also have
1685 * to kill a previous one. But we don't want to leave *without* a
1686 * connection if we came here from the connection layer, hence with a
1687 * connection. Thus we'll proceed in the following order :
1688 * 1: close but not release previous connection (handled by the caller)
1689 * 2: try to get a new connection
1690 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001691 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001692
Christopher Faulet61cc8522020-04-20 14:54:42 +02001693 /* 2- prepare new connection */
1694 cs = cs_new(NULL);
1695 if (!cs) {
1696 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1697 tcpcheck_get_step_id(check, rule));
1698 if (rule->comment)
1699 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1700 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1701 ret = TCPCHK_EVAL_STOP;
1702 goto out;
1703 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001704
Christopher Faulet61cc8522020-04-20 14:54:42 +02001705 /* 3- release and replace the old one on success */
1706 if (check->cs) {
1707 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001708 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1709 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001710
1711 /* We may have been scheduled to run, and the I/O handler
1712 * expects to have a cs, so remove the tasklet
1713 */
1714 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1715 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001716 }
1717
Christopher Faulet61cc8522020-04-20 14:54:42 +02001718 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001719
Christopher Faulet61cc8522020-04-20 14:54:42 +02001720 check->cs = cs;
1721 conn = cs->conn;
1722 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001723
Christopher Faulet61cc8522020-04-20 14:54:42 +02001724 /* Maybe there were an older connection we were waiting on */
1725 check->wait_list.events = 0;
1726 conn->target = s ? &s->obj_type : &proxy->obj_type;
1727
1728 /* no client address */
1729 if (!sockaddr_alloc(&conn->dst)) {
1730 status = SF_ERR_RESOURCE;
1731 goto fail_check;
1732 }
1733
1734 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001735 * addr if specified on the server. otherwise, use the server addr (it
1736 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001737 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001738 *conn->dst = (is_addr(&connect->addr)
1739 ? connect->addr
1740 : (is_addr(&check->addr) ? check->addr : s->addr));
1741 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001742
Christopher Faulet61cc8522020-04-20 14:54:42 +02001743 port = 0;
1744 if (!port && connect->port)
1745 port = connect->port;
1746 if (!port && connect->port_expr) {
1747 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001748
Christopher Faulet61cc8522020-04-20 14:54:42 +02001749 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1750 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1751 connect->port_expr, SMP_T_SINT);
1752 if (smp)
1753 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001754 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001755 if (!port && is_inet_addr(&connect->addr))
1756 port = get_host_port(&connect->addr);
1757 if (!port && check->port)
1758 port = check->port;
1759 if (!port && is_inet_addr(&check->addr))
1760 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001761 if (!port) {
1762 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001763 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001764 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001765 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001766
Christopher Faulet61cc8522020-04-20 14:54:42 +02001767 xprt = ((connect->options & TCPCHK_OPT_SSL)
1768 ? xprt_get(XPRT_SSL)
1769 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001770
Christopher Faulet61cc8522020-04-20 14:54:42 +02001771 conn_prepare(conn, proto, xprt);
1772 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001773
Christopher Faulet61cc8522020-04-20 14:54:42 +02001774 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001775 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001776 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001777 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001778
Christopher Faulet61cc8522020-04-20 14:54:42 +02001779 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1780 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001781 if (!next || next->action != TCPCHK_ACT_EXPECT)
1782 flags |= CONNECT_DELACK_ALWAYS;
1783 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001784 }
1785
Christopher Faulet61cc8522020-04-20 14:54:42 +02001786 if (status != SF_ERR_NONE)
1787 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001788
Christopher Faulet61cc8522020-04-20 14:54:42 +02001789 conn->flags |= CO_FL_PRIVATE;
1790 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001791
Christopher Faulet61cc8522020-04-20 14:54:42 +02001792 /* The mux may be initialized now if there isn't server attached to the
1793 * check (email alerts) or if there is a mux proto specified or if there
1794 * is no alpn.
1795 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001796 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1797 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001798 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001799
Christopher Faulet61cc8522020-04-20 14:54:42 +02001800 if (connect->mux_proto)
1801 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001802 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001803 mux_ops = check->mux_proto->mux;
1804 else {
1805 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1806 ? PROTO_MODE_HTTP
1807 : PROTO_MODE_TCP);
1808
1809 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001810 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001811 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1812 status = SF_ERR_INTERNAL;
1813 goto fail_check;
1814 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001815 }
1816
Christopher Faulet61cc8522020-04-20 14:54:42 +02001817#ifdef USE_OPENSSL
1818 if (connect->sni)
1819 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001820 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001821 ssl_sock_set_servername(conn, s->check.sni);
1822
1823 if (connect->alpn)
1824 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001825 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001826 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1827#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001828 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001829 conn->send_proxy_ofs = 1;
1830 conn->flags |= CO_FL_SOCKS4;
1831 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001832 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 +02001833 conn->send_proxy_ofs = 1;
1834 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001835 }
1836
Christopher Faulet61cc8522020-04-20 14:54:42 +02001837 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1838 conn->send_proxy_ofs = 1;
1839 conn->flags |= CO_FL_SEND_PROXY;
1840 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001841 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 +02001842 conn->send_proxy_ofs = 1;
1843 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001844 }
1845
Christopher Faulet61cc8522020-04-20 14:54:42 +02001846 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1847 /* Some servers don't like reset on close */
1848 fdtab[cs->conn->handle.fd].linger_risk = 0;
1849 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001850
Christopher Faulet61cc8522020-04-20 14:54:42 +02001851 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1852 if (xprt_add_hs(conn) < 0)
1853 status = SF_ERR_RESOURCE;
1854 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001855
Christopher Faulet61cc8522020-04-20 14:54:42 +02001856 fail_check:
1857 /* It can return one of :
1858 * - SF_ERR_NONE if everything's OK
1859 * - SF_ERR_SRVTO if there are no more servers
1860 * - SF_ERR_SRVCL if the connection was refused by the server
1861 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1862 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1863 * - SF_ERR_INTERNAL for any other purely internal errors
1864 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1865 * Note that we try to prevent the network stack from sending the ACK during the
1866 * connect() when a pure TCP check is used (without PROXY protocol).
1867 */
1868 switch (status) {
1869 case SF_ERR_NONE:
1870 /* we allow up to min(inter, timeout.connect) for a connection
1871 * to establish but only when timeout.check is set as it may be
1872 * to short for a full check otherwise
1873 */
1874 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001875
Christopher Faulet61cc8522020-04-20 14:54:42 +02001876 if (proxy->timeout.check && proxy->timeout.connect) {
1877 int t_con = tick_add(now_ms, proxy->timeout.connect);
1878 t->expire = tick_first(t->expire, t_con);
1879 }
1880 break;
1881 case SF_ERR_SRVTO: /* ETIMEDOUT */
1882 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1883 case SF_ERR_PRXCOND:
1884 case SF_ERR_RESOURCE:
1885 case SF_ERR_INTERNAL:
1886 chk_report_conn_err(check, errno, 0);
1887 ret = TCPCHK_EVAL_STOP;
1888 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001889 }
1890
Christopher Faulet61cc8522020-04-20 14:54:42 +02001891 /* don't do anything until the connection is established */
1892 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001893 if (conn->mux) {
1894 if (next && next->action == TCPCHK_ACT_SEND)
1895 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1896 else
1897 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1898 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001899 ret = TCPCHK_EVAL_WAIT;
1900 goto out;
1901 }
1902
1903 out:
1904 if (conn && check->result == CHK_RES_FAILED)
1905 conn->flags |= CO_FL_ERROR;
1906 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001907}
1908
Christopher Faulet61cc8522020-04-20 14:54:42 +02001909/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1910 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1911 * TCPCHK_EVAL_STOP if an error occurred.
1912 */
1913static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001914{
1915 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001916 struct tcpcheck_send *send = &rule->send;
1917 struct conn_stream *cs = check->cs;
1918 struct connection *conn = cs_conn(cs);
1919 struct buffer *tmp = NULL;
1920 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001921
Christopher Faulet61cc8522020-04-20 14:54:42 +02001922 /* reset the read & write buffer */
1923 b_reset(&check->bi);
1924 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001925
Christopher Faulet61cc8522020-04-20 14:54:42 +02001926 switch (send->type) {
1927 case TCPCHK_SEND_STRING:
1928 case TCPCHK_SEND_BINARY:
1929 if (istlen(send->data) >= b_size(&check->bo)) {
1930 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1931 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1932 tcpcheck_get_step_id(check, rule));
1933 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1934 ret = TCPCHK_EVAL_STOP;
1935 goto out;
1936 }
1937 b_putist(&check->bo, send->data);
1938 break;
1939 case TCPCHK_SEND_STRING_LF:
1940 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1941 if (!b_data(&check->bo))
1942 goto out;
1943 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001944 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001945 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001946
Christopher Faulet61cc8522020-04-20 14:54:42 +02001947 tmp = alloc_trash_chunk();
1948 if (!tmp)
1949 goto error_lf;
1950 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1951 if (!b_data(tmp))
1952 goto out;
1953 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001954 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001955 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001956 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001957 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001958 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001959 case TCPCHK_SEND_HTTP: {
1960 struct htx_sl *sl;
1961 struct ist meth, uri, vsn, clen, body;
1962 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001963
Christopher Faulet61cc8522020-04-20 14:54:42 +02001964 tmp = alloc_trash_chunk();
1965 if (!tmp)
1966 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001967
Christopher Faulet61cc8522020-04-20 14:54:42 +02001968 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1969 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1970 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001971 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1972 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1973 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1974 }
1975 else
1976 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001977 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001978
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001979 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1980 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001981 slflags |= HTX_SL_F_VER_11;
1982 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1983 if (!isttest(send->http.body))
1984 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001985
Christopher Faulet61cc8522020-04-20 14:54:42 +02001986 htx = htx_from_buf(&check->bo);
1987 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1988 if (!sl)
1989 goto error_htx;
1990 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001991 if (!http_update_host(htx, sl, uri))
1992 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001993
Christopher Faulet61cc8522020-04-20 14:54:42 +02001994 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1995 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001996 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001997
Christopher Faulet61cc8522020-04-20 14:54:42 +02001998 list_for_each_entry(hdr, &send->http.hdrs, list) {
1999 chunk_reset(tmp);
2000 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2001 if (!b_data(tmp))
2002 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002003 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2004 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002005 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002006 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2007 if (!http_update_authority(htx, sl, hdr_value))
2008 goto error_htx;
2009 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002010 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002011
Christopher Faulet61cc8522020-04-20 14:54:42 +02002012 }
2013 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2014 chunk_reset(tmp);
2015 httpchk_build_status_header(check->server, tmp);
2016 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2017 goto error_htx;
2018 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002019
2020
2021 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2022 chunk_reset(tmp);
2023 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2024 body = ist2(b_orig(tmp), b_data(tmp));
2025 }
2026 else
2027 body = send->http.body;
2028 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2029
2030 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2031 !htx_add_header(htx, ist("Content-length"), clen))
2032 goto error_htx;
2033
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002034
Christopher Faulet61cc8522020-04-20 14:54:42 +02002035 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002036 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002037 !htx_add_endof(htx, HTX_BLK_EOM))
2038 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002039
Christopher Faulet61cc8522020-04-20 14:54:42 +02002040 htx_to_buf(htx, &check->bo);
2041 break;
2042 }
2043 case TCPCHK_SEND_UNDEF:
2044 /* Should never happen. */
2045 ret = TCPCHK_EVAL_STOP;
2046 goto out;
2047 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002048
Christopher Faulet6d471212020-04-22 11:09:25 +02002049
2050 if (conn->mux->snd_buf(cs, &check->bo,
2051 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002052 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002053 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002054 goto out;
2055 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002056 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002057 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002058 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2059 ret = TCPCHK_EVAL_WAIT;
2060 goto out;
2061 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002062
Christopher Faulet61cc8522020-04-20 14:54:42 +02002063 out:
2064 free_trash_chunk(tmp);
2065 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002066
Christopher Faulet61cc8522020-04-20 14:54:42 +02002067 error_htx:
2068 if (htx) {
2069 htx_reset(htx);
2070 htx_to_buf(htx, &check->bo);
2071 }
2072 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2073 tcpcheck_get_step_id(check, rule));
2074 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2075 ret = TCPCHK_EVAL_STOP;
2076 goto out;
2077
2078 error_lf:
2079 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2080 tcpcheck_get_step_id(check, rule));
2081 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2082 ret = TCPCHK_EVAL_STOP;
2083 goto out;
2084
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002085}
2086
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002087/* Try to receive data before evaluating a tcp-check expect rule. Returns
2088 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002089 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2090 * TCPCHK_EVAL_STOP if an error occurred.
2091 */
2092static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002093{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002094 struct conn_stream *cs = check->cs;
2095 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002096 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002097 size_t max, read, cur_read = 0;
2098 int is_empty;
2099 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002100
Christopher Faulet61cc8522020-04-20 14:54:42 +02002101 if (check->wait_list.events & SUB_RETRY_RECV)
2102 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002103
Christopher Faulet61cc8522020-04-20 14:54:42 +02002104 if (cs->flags & CS_FL_EOS)
2105 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002106
Christopher Faulet61cc8522020-04-20 14:54:42 +02002107 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002108
Christopher Faulet61cc8522020-04-20 14:54:42 +02002109 /* prepare to detect if the mux needs more room */
2110 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002111
Christopher Faulet61cc8522020-04-20 14:54:42 +02002112 while ((cs->flags & CS_FL_RCV_MORE) ||
2113 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2114 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2115 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2116 cur_read += read;
2117 if (!read ||
2118 (cs->flags & CS_FL_WANT_ROOM) ||
2119 (--read_poll <= 0) ||
2120 (read < max && read >= global.tune.recv_enough))
2121 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002122 }
2123
Christopher Faulet61cc8522020-04-20 14:54:42 +02002124 end_recv:
2125 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2126 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2127 /* Report network errors only if we got no other data. Otherwise
2128 * we'll let the upper layers decide whether the response is OK
2129 * or not. It is very common that an RST sent by the server is
2130 * reported as an error just after the last data chunk.
2131 */
2132 goto stop;
2133 }
2134 if (!cur_read) {
2135 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2136 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2137 goto wait_more_data;
2138 }
2139 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002140 int status;
2141
Christopher Faulet61cc8522020-04-20 14:54:42 +02002142 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2143 tcpcheck_get_step_id(check, rule));
2144 if (rule->comment)
2145 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002146
2147 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2148 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002149 goto stop;
2150 }
2151 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002152
2153 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002154 return ret;
2155
Christopher Faulet61cc8522020-04-20 14:54:42 +02002156 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002157 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002158 goto out;
2159
2160 wait_more_data:
2161 ret = TCPCHK_EVAL_WAIT;
2162 goto out;
2163}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002164
Christopher Faulet61cc8522020-04-20 14:54:42 +02002165/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2166 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2167 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2168 * error occurred.
2169 */
2170static 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 +02002171{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002172 struct htx *htx = htxbuf(&check->bi);
2173 struct htx_sl *sl;
2174 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002175 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002176 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002177 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002178 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002179 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002180 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002181
Christopher Faulet61cc8522020-04-20 14:54:42 +02002182 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002183
Christopher Faulet61cc8522020-04-20 14:54:42 +02002184 if (htx->flags & HTX_FL_PARSING_ERROR) {
2185 status = HCHK_STATUS_L7RSP;
2186 goto error;
2187 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002188
Christopher Faulet61cc8522020-04-20 14:54:42 +02002189 if (htx_is_empty(htx)) {
2190 if (last_read) {
2191 status = HCHK_STATUS_L7RSP;
2192 goto error;
2193 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002194 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002195 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002196
Christopher Faulet61cc8522020-04-20 14:54:42 +02002197 sl = http_get_stline(htx);
2198 check->code = sl->info.res.status;
2199
2200 if (check->server &&
2201 (check->server->proxy->options & PR_O_DISABLE404) &&
2202 (check->server->next_state != SRV_ST_STOPPED) &&
2203 (check->code == 404)) {
2204 /* 404 may be accepted as "stopping" only if the server was up */
2205 goto out;
2206 }
2207
2208 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2209 /* Make GCC happy ; initialize match to a failure state. */
2210 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002211 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002212
2213 switch (expect->type) {
2214 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002215 match = 0;
2216 for (i = 0; i < expect->codes.num; i++) {
2217 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2218 sl->info.res.status <= expect->codes.codes[i][1]) {
2219 match = 1;
2220 break;
2221 }
2222 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002223
2224 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002225 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002226 if (LIST_ISEMPTY(&expect->onerror_fmt))
2227 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002228 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002229 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002230 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2231
2232 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002233 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002234 if (LIST_ISEMPTY(&expect->onerror_fmt))
2235 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002236 break;
2237
Christopher Faulet39708192020-05-05 10:47:36 +02002238 case TCPCHK_EXPECT_HTTP_HEADER: {
2239 struct http_hdr_ctx ctx;
2240 struct ist npat, vpat, value;
2241 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2242
2243 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2244 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002245 if (!nbuf) {
2246 status = HCHK_STATUS_L7RSP;
2247 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002248 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002249 }
Christopher Faulet39708192020-05-05 10:47:36 +02002250 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 +02002251 if (!b_data(nbuf)) {
2252 status = HCHK_STATUS_L7RSP;
2253 desc = ist("log-format string evaluated to an empty string");
2254 goto error;
2255 }
Christopher Faulet39708192020-05-05 10:47:36 +02002256 npat = ist2(b_orig(nbuf), b_data(nbuf));
2257 }
2258 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2259 npat = expect->hdr.name;
2260
2261 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2262 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002263 if (!vbuf) {
2264 status = HCHK_STATUS_L7RSP;
2265 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002266 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002267 }
Christopher Faulet39708192020-05-05 10:47:36 +02002268 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 +02002269 if (!b_data(vbuf)) {
2270 status = HCHK_STATUS_L7RSP;
2271 desc = ist("log-format string evaluated to an empty string");
2272 goto error;
2273 }
Christopher Faulet39708192020-05-05 10:47:36 +02002274 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2275 }
2276 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2277 vpat = expect->hdr.value;
2278
2279 match = 0;
2280 ctx.blk = NULL;
2281 while (1) {
2282 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2283 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2284 if (!http_find_str_header(htx, npat, &ctx, full))
2285 goto end_of_match;
2286 break;
2287 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2288 if (!http_find_pfx_header(htx, npat, &ctx, full))
2289 goto end_of_match;
2290 break;
2291 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2292 if (!http_find_sfx_header(htx, npat, &ctx, full))
2293 goto end_of_match;
2294 break;
2295 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2296 if (!http_find_sub_header(htx, npat, &ctx, full))
2297 goto end_of_match;
2298 break;
2299 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2300 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2301 goto end_of_match;
2302 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002303 default:
2304 /* should never happen */
2305 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002306 }
2307
Christopher Faulet083eff32020-05-07 15:41:39 +02002308 /* A header has matched the name pattern, let's test its
2309 * value now (always defined from there). If there is no
2310 * value pattern, it is a good match.
2311 */
2312
Christopher Faulet39708192020-05-05 10:47:36 +02002313 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2314 match = 1;
2315 goto end_of_match;
2316 }
2317
2318 value = ctx.value;
2319 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2320 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2321 if (isteq(value, vpat)) {
2322 match = 1;
2323 goto end_of_match;
2324 }
2325 break;
2326 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2327 if (istlen(value) < istlen(vpat))
2328 break;
2329 value = ist2(istptr(value), istlen(vpat));
2330 if (isteq(value, vpat)) {
2331 match = 1;
2332 goto end_of_match;
2333 }
2334 break;
2335 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2336 if (istlen(value) < istlen(vpat))
2337 break;
2338 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2339 if (isteq(value, vpat)) {
2340 match = 1;
2341 goto end_of_match;
2342 }
2343 break;
2344 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2345 if (isttest(istist(value, vpat))) {
2346 match = 1;
2347 goto end_of_match;
2348 }
2349 break;
2350 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2351 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2352 match = 1;
2353 goto end_of_match;
2354 }
2355 break;
2356 }
2357 }
2358
2359 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002360 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002361 if (LIST_ISEMPTY(&expect->onerror_fmt))
2362 desc = htx_sl_res_reason(sl);
2363 break;
2364 }
2365
Christopher Faulet61cc8522020-04-20 14:54:42 +02002366 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002367 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002368 case TCPCHK_EXPECT_HTTP_BODY_LF:
2369 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002370 chunk_reset(&trash);
2371 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2372 enum htx_blk_type type = htx_get_blk_type(blk);
2373
2374 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2375 break;
2376 if (type == HTX_BLK_DATA) {
2377 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2378 break;
2379 }
2380 }
2381
2382 if (!b_data(&trash)) {
2383 if (!last_read)
2384 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002385 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002386 if (LIST_ISEMPTY(&expect->onerror_fmt))
2387 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002388 goto error;
2389 }
2390
Christopher Fauletaaab0832020-05-05 15:54:22 +02002391 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2392 tmp = alloc_trash_chunk();
2393 if (!tmp) {
2394 status = HCHK_STATUS_L7RSP;
2395 desc = ist("Failed to allocate buffer to eval log-format string");
2396 goto error;
2397 }
2398 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2399 if (!b_data(tmp)) {
2400 status = HCHK_STATUS_L7RSP;
2401 desc = ist("log-format string evaluated to an empty string");
2402 goto error;
2403 }
2404 }
2405
Christopher Faulet61cc8522020-04-20 14:54:42 +02002406 if (!last_read &&
2407 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002408 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002409 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2410 ret = TCPCHK_EVAL_WAIT;
2411 goto out;
2412 }
2413
2414 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002415 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002416 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2417 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002418 else
2419 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2420
2421 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002422 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002423 if (LIST_ISEMPTY(&expect->onerror_fmt))
2424 desc = (inverse
2425 ? ist("HTTP check matched unwanted content")
2426 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002427 break;
2428
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002429
Christopher Faulet61cc8522020-04-20 14:54:42 +02002430 default:
2431 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002432 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002433 goto error;
2434 }
2435
Christopher Faulet61cc8522020-04-20 14:54:42 +02002436 /* Wait for more data on mismatch only if no minimum is defined (-1),
2437 * otherwise the absence of match is already conclusive.
2438 */
2439 if (!match && !last_read && (expect->min_recv == -1)) {
2440 ret = TCPCHK_EVAL_WAIT;
2441 goto out;
2442 }
2443
2444 if (!(match ^ inverse))
2445 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002446
2447 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002448 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002449 free_trash_chunk(nbuf);
2450 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002451 free_trash_chunk(msg);
2452 return ret;
2453
2454 error:
2455 ret = TCPCHK_EVAL_STOP;
2456 msg = alloc_trash_chunk();
2457 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002458 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002459 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2460 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002461
2462 wait_more_data:
2463 ret = TCPCHK_EVAL_WAIT;
2464 goto out;
2465}
2466
Christopher Faulet61cc8522020-04-20 14:54:42 +02002467/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2468 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2469 * if an error occurred.
2470 */
2471static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2472{
2473 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2474 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002475 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002476 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002477 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002478 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002479
Christopher Faulet61cc8522020-04-20 14:54:42 +02002480 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002481
Christopher Faulet61cc8522020-04-20 14:54:42 +02002482 /* The current expect might need more data than the previous one, check again
2483 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002484 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002485 if (!last_read) {
2486 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2487 (b_data(&check->bi) < istlen(expect->data))) {
2488 ret = TCPCHK_EVAL_WAIT;
2489 goto out;
2490 }
2491 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2492 ret = TCPCHK_EVAL_WAIT;
2493 goto out;
2494 }
2495 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002496
Christopher Faulet61cc8522020-04-20 14:54:42 +02002497 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2498 /* Make GCC happy ; initialize match to a failure state. */
2499 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002500 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002501
Christopher Faulet61cc8522020-04-20 14:54:42 +02002502 switch (expect->type) {
2503 case TCPCHK_EXPECT_STRING:
2504 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002505 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 +02002506 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002507 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002508 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 +02002509 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002510
Christopher Faulet67a23452020-05-05 18:10:01 +02002511 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002512 chunk_reset(&trash);
2513 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002514 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002515 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002516
2517 case TCPCHK_EXPECT_STRING_LF:
2518 case TCPCHK_EXPECT_BINARY_LF:
2519 match = 0;
2520 tmp = alloc_trash_chunk();
2521 if (!tmp) {
2522 status = HCHK_STATUS_L7RSP;
2523 desc = ist("Failed to allocate buffer to eval format string");
2524 goto error;
2525 }
2526 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2527 if (!b_data(tmp)) {
2528 status = HCHK_STATUS_L7RSP;
2529 desc = ist("log-format string evaluated to an empty string");
2530 goto error;
2531 }
2532 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2533 int len = tmp->data;
2534 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2535 status = HCHK_STATUS_L7RSP;
2536 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2537 goto error;
2538 }
2539 tmp->data = len;
2540 }
2541 if (b_data(&check->bi) < tmp->data) {
2542 if (!last_read) {
2543 ret = TCPCHK_EVAL_WAIT;
2544 goto out;
2545 }
2546 break;
2547 }
2548 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2549 break;
2550
Christopher Faulet61cc8522020-04-20 14:54:42 +02002551 case TCPCHK_EXPECT_CUSTOM:
2552 if (expect->custom)
2553 ret = expect->custom(check, rule, last_read);
2554 goto out;
2555 default:
2556 /* Should never happen. */
2557 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002558 goto out;
2559 }
2560
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002561
Christopher Faulet61cc8522020-04-20 14:54:42 +02002562 /* Wait for more data on mismatch only if no minimum is defined (-1),
2563 * otherwise the absence of match is already conclusive.
2564 */
2565 if (!match && !last_read && (expect->min_recv == -1)) {
2566 ret = TCPCHK_EVAL_WAIT;
2567 goto out;
2568 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002569
Christopher Faulet61cc8522020-04-20 14:54:42 +02002570 /* Result as expected, next rule. */
2571 if (match ^ inverse)
2572 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002573
Christopher Fauletaaab0832020-05-05 15:54:22 +02002574 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002575 /* From this point on, we matched something we did not want, this is an error state. */
2576 ret = TCPCHK_EVAL_STOP;
2577 msg = alloc_trash_chunk();
2578 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002579 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002580 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002581 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002582
Christopher Faulet61cc8522020-04-20 14:54:42 +02002583 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002584 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002585 return ret;
2586}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002587
Christopher Faulet61cc8522020-04-20 14:54:42 +02002588/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002589 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002590 * waits.
2591 */
2592static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2593{
2594 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2595 struct act_rule *act_rule;
2596 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002597
Christopher Faulet61cc8522020-04-20 14:54:42 +02002598 act_rule =rule->action_kw.rule;
2599 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2600 if (act_ret != ACT_RET_CONT) {
2601 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2602 tcpcheck_get_step_id(check, rule));
2603 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2604 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002605 }
2606
Christopher Faulet61cc8522020-04-20 14:54:42 +02002607 return ret;
2608}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002609
Christopher Faulet61cc8522020-04-20 14:54:42 +02002610/* Executes a tcp-check ruleset. Note that this is called both from the
2611 * connection's wake() callback and from the check scheduling task. It returns
2612 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2613 * presenting the risk of an fd replacement.
2614 *
2615 * Please do NOT place any return statement in this function and only leave
2616 * via the out_end_tcpcheck label after setting retcode.
2617 */
2618static int tcpcheck_main(struct check *check)
2619{
2620 struct tcpcheck_rule *rule;
2621 struct conn_stream *cs = check->cs;
2622 struct connection *conn = cs_conn(cs);
2623 int must_read = 1, last_read = 0;
2624 int ret, retcode = 0;
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002625 enum tcpcheck_eval_ret eval_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002626
Christopher Faulet61cc8522020-04-20 14:54:42 +02002627 /* here, we know that the check is complete or that it failed */
2628 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002629 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002630
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002631 /* Note: the conn-stream and the connection may only be undefined before
2632 * the first rule evaluation (it is always a connect rule) or when the
2633 * conn-stream allocation failed on the first connect.
2634 */
2635
Christopher Faulet61cc8522020-04-20 14:54:42 +02002636 /* 1- check for connection error, if any */
2637 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2638 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002639
Christopher Faulet61cc8522020-04-20 14:54:42 +02002640 /* 2- check if we are waiting for the connection establishment. It only
2641 * happens during TCPCHK_ACT_CONNECT. */
2642 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002643 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002644 struct tcpcheck_rule *next;
2645
2646 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2647 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002648 if (!(check->wait_list.events & SUB_RETRY_SEND))
2649 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002650 goto out;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002651 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002652 else {
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002653 eval_ret = tcpcheck_eval_recv(check, check->current_step);
2654 if (eval_ret == TCPCHK_EVAL_STOP)
2655 goto out_end_tcpcheck;
2656 else if (eval_ret == TCPCHK_EVAL_WAIT)
2657 goto out;
2658 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2659 must_read = 0;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002660 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002661 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002662 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002663 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002664
2665 /* 3- check for pending outgoing data. It only happens during
2666 * TCPCHK_ACT_SEND. */
2667 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002668 if (b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002669 /* We're already waiting to be able to send, give up */
2670 if (check->wait_list.events & SUB_RETRY_SEND)
2671 goto out;
2672
Christopher Faulet6d471212020-04-22 11:09:25 +02002673 ret = conn->mux->snd_buf(cs, &check->bo,
2674 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002675 if (ret <= 0) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002676 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002677 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002678 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002679 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002680 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002681 goto out;
2682 }
2683 }
2684 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002685 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002686
Christopher Faulet61cc8522020-04-20 14:54:42 +02002687 /* 4- check if a rule must be resume. It happens if check->current_step
2688 * is defined. */
2689 else if (check->current_step)
2690 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002691
Christopher Faulet61cc8522020-04-20 14:54:42 +02002692 /* 5- It is the first evaluation. We must create a session and preset
2693 * tcp-check variables */
2694 else {
2695 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002696
Christopher Faulet61cc8522020-04-20 14:54:42 +02002697 /* First evaluation, create a session */
2698 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2699 if (!check->sess) {
2700 chunk_printf(&trash, "TCPCHK error allocating check session");
2701 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2702 goto out_end_tcpcheck;
2703 }
2704 vars_init(&check->vars, SCOPE_CHECK);
2705 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002706
Christopher Faulet61cc8522020-04-20 14:54:42 +02002707 /* Preset tcp-check variables */
2708 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2709 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002710
Christopher Faulet61cc8522020-04-20 14:54:42 +02002711 memset(&smp, 0, sizeof(smp));
2712 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2713 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002714 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002715 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002716 }
2717
Christopher Faulet61cc8522020-04-20 14:54:42 +02002718 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002719
Christopher Faulet61cc8522020-04-20 14:54:42 +02002720 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002721 check->code = 0;
2722 switch (rule->action) {
2723 case TCPCHK_ACT_CONNECT:
2724 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002725
Christopher Faulet61cc8522020-04-20 14:54:42 +02002726 /* close but not release yet previous connection */
2727 if (check->cs) {
2728 cs_close(check->cs);
2729 retcode = -1; /* do not reuse the fd in the caller! */
2730 }
2731 eval_ret = tcpcheck_eval_connect(check, rule);
Christopher Faulet3cbdd222020-05-26 11:14:50 +02002732
2733 /* Refresh conn-stream and connection */
2734 cs = check->cs;
2735 conn = cs_conn(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002736 must_read = 1; last_read = 0;
2737 break;
2738 case TCPCHK_ACT_SEND:
2739 check->current_step = rule;
2740 eval_ret = tcpcheck_eval_send(check, rule);
2741 must_read = 1;
2742 break;
2743 case TCPCHK_ACT_EXPECT:
2744 check->current_step = rule;
2745 if (must_read) {
2746 if (check->proxy->timeout.check)
2747 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002748
Christopher Faulet61cc8522020-04-20 14:54:42 +02002749 eval_ret = tcpcheck_eval_recv(check, rule);
2750 if (eval_ret == TCPCHK_EVAL_STOP)
2751 goto out_end_tcpcheck;
2752 else if (eval_ret == TCPCHK_EVAL_WAIT)
2753 goto out;
2754 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2755 must_read = 0;
2756 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002757
Christopher Faulet61cc8522020-04-20 14:54:42 +02002758 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2759 ? tcpcheck_eval_expect_http(check, rule, last_read)
2760 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002761
Christopher Faulet61cc8522020-04-20 14:54:42 +02002762 if (eval_ret == TCPCHK_EVAL_WAIT) {
2763 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002764 if (!(check->wait_list.events & SUB_RETRY_RECV))
2765 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002766 }
2767 break;
2768 case TCPCHK_ACT_ACTION_KW:
2769 /* Don't update the current step */
2770 eval_ret = tcpcheck_eval_action_kw(check, rule);
2771 break;
2772 default:
2773 /* Otherwise, just go to the next one and don't update
2774 * the current step
2775 */
2776 eval_ret = TCPCHK_EVAL_CONTINUE;
2777 break;
2778 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002779
Christopher Faulet61cc8522020-04-20 14:54:42 +02002780 switch (eval_ret) {
2781 case TCPCHK_EVAL_CONTINUE:
2782 break;
2783 case TCPCHK_EVAL_WAIT:
2784 goto out;
2785 case TCPCHK_EVAL_STOP:
2786 goto out_end_tcpcheck;
2787 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002788 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002789
Christopher Faulet61cc8522020-04-20 14:54:42 +02002790 /* All rules was evaluated */
2791 if (check->current_step) {
2792 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002793
Christopher Faulet61cc8522020-04-20 14:54:42 +02002794 if (rule->action == TCPCHK_ACT_EXPECT) {
2795 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002796 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002797
Christopher Faulet61cc8522020-04-20 14:54:42 +02002798 if (check->server &&
2799 (check->server->proxy->options & PR_O_DISABLE404) &&
2800 (check->server->next_state != SRV_ST_STOPPED) &&
2801 (check->code == 404)) {
2802 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2803 goto out_end_tcpcheck;
2804 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002805
Christopher Faulet61cc8522020-04-20 14:54:42 +02002806 msg = alloc_trash_chunk();
2807 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002808 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002809 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2810 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002811 free_trash_chunk(msg);
2812 }
2813 else if (rule->action == TCPCHK_ACT_CONNECT) {
2814 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002815 enum healthcheck_status status = HCHK_STATUS_L4OK;
2816#ifdef USE_OPENSSL
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002817 if (ssl_sock_is_ssl(conn))
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002818 status = HCHK_STATUS_L6OK;
2819#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002820 set_server_check_status(check, status, msg);
2821 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002822 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002823 else
2824 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002825
Christopher Faulet61cc8522020-04-20 14:54:42 +02002826 out_end_tcpcheck:
2827 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2828 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002829
Christopher Faulet61cc8522020-04-20 14:54:42 +02002830 out:
2831 return retcode;
2832}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002833
Christopher Faulet14cd3162020-04-16 14:50:06 +02002834
Christopher Faulet61cc8522020-04-20 14:54:42 +02002835/**************************************************************************/
2836/************** Health-checks based on an external process ****************/
2837/**************************************************************************/
2838static struct list pid_list = LIST_HEAD_INIT(pid_list);
2839static struct pool_head *pool_head_pid_list;
2840__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002841
Christopher Faulet61cc8522020-04-20 14:54:42 +02002842struct extcheck_env {
2843 char *name; /* environment variable name */
2844 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2845};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002846
Christopher Faulet61cc8522020-04-20 14:54:42 +02002847/* environment variables memory requirement for different types of data */
2848#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2849 * such environment variables are not updatable. */
2850#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2851#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2852#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002853
Christopher Faulet61cc8522020-04-20 14:54:42 +02002854/* external checks environment variables */
2855enum {
2856 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002857
Christopher Faulet61cc8522020-04-20 14:54:42 +02002858 /* Proxy specific environment variables */
2859 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2860 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2861 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2862 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002863
Christopher Faulet61cc8522020-04-20 14:54:42 +02002864 /* Server specific environment variables */
2865 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2866 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2867 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2868 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2869 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2870 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002871
Christopher Faulet61cc8522020-04-20 14:54:42 +02002872 EXTCHK_SIZE
2873};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002874
Christopher Faulet61cc8522020-04-20 14:54:42 +02002875const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2876 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2877 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2878 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2879 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2880 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2881 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2882 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2883 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2884 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2885 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2886 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2887};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002888
Christopher Faulet61cc8522020-04-20 14:54:42 +02002889void block_sigchld(void)
2890{
2891 sigset_t set;
2892 sigemptyset(&set);
2893 sigaddset(&set, SIGCHLD);
2894 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2895}
Willy Tarreaube373152018-09-06 11:45:30 +02002896
Christopher Faulet61cc8522020-04-20 14:54:42 +02002897void unblock_sigchld(void)
2898{
2899 sigset_t set;
2900 sigemptyset(&set);
2901 sigaddset(&set, SIGCHLD);
2902 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002903}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002904
Christopher Faulet61cc8522020-04-20 14:54:42 +02002905static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002906{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002907 struct pid_list *elem;
2908 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002909
Christopher Faulet61cc8522020-04-20 14:54:42 +02002910 elem = pool_alloc(pool_head_pid_list);
2911 if (!elem)
2912 return NULL;
2913 elem->pid = pid;
2914 elem->t = t;
2915 elem->exited = 0;
2916 check->curpid = elem;
2917 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002918
Christopher Faulet61cc8522020-04-20 14:54:42 +02002919 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2920 LIST_ADD(&pid_list, &elem->list);
2921 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002922
Christopher Faulet61cc8522020-04-20 14:54:42 +02002923 return elem;
2924}
Christopher Faulete5870d82020-04-15 11:32:03 +02002925
Christopher Faulet61cc8522020-04-20 14:54:42 +02002926static void pid_list_del(struct pid_list *elem)
2927{
2928 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002929
Christopher Faulet61cc8522020-04-20 14:54:42 +02002930 if (!elem)
2931 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002932
Christopher Faulet61cc8522020-04-20 14:54:42 +02002933 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2934 LIST_DEL(&elem->list);
2935 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002936
Christopher Faulet61cc8522020-04-20 14:54:42 +02002937 if (!elem->exited)
2938 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002939
Christopher Faulet61cc8522020-04-20 14:54:42 +02002940 check = elem->t->context;
2941 check->curpid = NULL;
2942 pool_free(pool_head_pid_list, elem);
2943}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002944
Christopher Faulet61cc8522020-04-20 14:54:42 +02002945/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2946static void pid_list_expire(pid_t pid, int status)
2947{
2948 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002949
Christopher Faulet61cc8522020-04-20 14:54:42 +02002950 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2951 list_for_each_entry(elem, &pid_list, list) {
2952 if (elem->pid == pid) {
2953 elem->t->expire = now_ms;
2954 elem->status = status;
2955 elem->exited = 1;
2956 task_wakeup(elem->t, TASK_WOKEN_IO);
2957 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002958 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002959 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002960 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2961}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002962
Christopher Faulet61cc8522020-04-20 14:54:42 +02002963static void sigchld_handler(struct sig_handler *sh)
2964{
2965 pid_t pid;
2966 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002967
Christopher Faulet61cc8522020-04-20 14:54:42 +02002968 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2969 pid_list_expire(pid, status);
2970}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002971
Christopher Faulet61cc8522020-04-20 14:54:42 +02002972static int init_pid_list(void)
2973{
2974 if (pool_head_pid_list != NULL)
2975 /* Nothing to do */
2976 return 0;
2977
2978 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2979 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2980 strerror(errno));
2981 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002982 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002983
Christopher Faulet61cc8522020-04-20 14:54:42 +02002984 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2985 if (pool_head_pid_list == NULL) {
2986 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2987 strerror(errno));
2988 return 1;
2989 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002990
Christopher Faulet61cc8522020-04-20 14:54:42 +02002991 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002992}
2993
Christopher Faulet61cc8522020-04-20 14:54:42 +02002994/* helper macro to set an environment variable and jump to a specific label on failure. */
2995#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002996
Christopher Faulet61cc8522020-04-20 14:54:42 +02002997/*
2998 * helper function to allocate enough memory to store an environment variable.
2999 * It will also check that the environment variable is updatable, and silently
3000 * fail if not.
3001 */
3002static int extchk_setenv(struct check *check, int idx, const char *value)
3003{
3004 int len, ret;
3005 char *envname;
3006 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003007
Christopher Faulet61cc8522020-04-20 14:54:42 +02003008 if (idx < 0 || idx >= EXTCHK_SIZE) {
3009 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
3010 return 1;
3011 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003012
Christopher Faulet61cc8522020-04-20 14:54:42 +02003013 envname = extcheck_envs[idx].name;
3014 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003015
Christopher Faulet61cc8522020-04-20 14:54:42 +02003016 /* Check if the environment variable is already set, and silently reject
3017 * the update if this one is not updatable. */
3018 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3019 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003020
Christopher Faulet61cc8522020-04-20 14:54:42 +02003021 /* Instead of sending NOT_USED, sending an empty value is preferable */
3022 if (strcmp(value, "NOT_USED") == 0) {
3023 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003024 }
3025
Christopher Faulet61cc8522020-04-20 14:54:42 +02003026 len = strlen(envname) + 1;
3027 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3028 len += strlen(value);
3029 else
3030 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003031
Christopher Faulet61cc8522020-04-20 14:54:42 +02003032 if (!check->envp[idx])
3033 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003034
Christopher Faulet61cc8522020-04-20 14:54:42 +02003035 if (!check->envp[idx]) {
3036 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3037 return 1;
3038 }
3039 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3040 if (ret < 0) {
3041 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3042 return 1;
3043 }
3044 else if (ret > len) {
3045 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3046 return 1;
3047 }
3048 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003049}
3050
Christopher Faulet61cc8522020-04-20 14:54:42 +02003051static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003052{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003053 struct server *s = check->server;
3054 struct proxy *px = s->proxy;
3055 struct listener *listener = NULL, *l;
3056 int i;
3057 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3058 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003059
Christopher Faulet61cc8522020-04-20 14:54:42 +02003060 list_for_each_entry(l, &px->conf.listeners, by_fe)
3061 /* Use the first INET, INET6 or UNIX listener */
3062 if (l->addr.ss_family == AF_INET ||
3063 l->addr.ss_family == AF_INET6 ||
3064 l->addr.ss_family == AF_UNIX) {
3065 listener = l;
3066 break;
3067 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003068
Christopher Faulet61cc8522020-04-20 14:54:42 +02003069 check->curpid = NULL;
3070 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3071 if (!check->envp) {
3072 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3073 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003074 }
3075
Christopher Faulet61cc8522020-04-20 14:54:42 +02003076 check->argv = calloc(6, sizeof(char *));
3077 if (!check->argv) {
3078 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3079 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003080 }
3081
Christopher Faulet61cc8522020-04-20 14:54:42 +02003082 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003083
Christopher Faulet61cc8522020-04-20 14:54:42 +02003084 if (!listener) {
3085 check->argv[1] = strdup("NOT_USED");
3086 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003087 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003088 else if (listener->addr.ss_family == AF_INET ||
3089 listener->addr.ss_family == AF_INET6) {
3090 addr_to_str(&listener->addr, buf, sizeof(buf));
3091 check->argv[1] = strdup(buf);
3092 port_to_str(&listener->addr, buf, sizeof(buf));
3093 check->argv[2] = strdup(buf);
3094 }
3095 else if (listener->addr.ss_family == AF_UNIX) {
3096 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003097
Christopher Faulet61cc8522020-04-20 14:54:42 +02003098 un = (struct sockaddr_un *)&listener->addr;
3099 check->argv[1] = strdup(un->sun_path);
3100 check->argv[2] = strdup("NOT_USED");
3101 }
3102 else {
3103 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3104 goto err;
3105 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003106
Christopher Faulet61cc8522020-04-20 14:54:42 +02003107 if (!check->argv[1] || !check->argv[2]) {
3108 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3109 goto err;
3110 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003111
Christopher Faulet61cc8522020-04-20 14:54:42 +02003112 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3113 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3114 if (!check->argv[3] || !check->argv[4]) {
3115 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3116 goto err;
3117 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003118
Christopher Faulet61cc8522020-04-20 14:54:42 +02003119 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3120 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3121 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003122
Christopher Faulet61cc8522020-04-20 14:54:42 +02003123 for (i = 0; i < 5; i++) {
3124 if (!check->argv[i]) {
3125 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3126 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003127 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003128 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003129
Christopher Faulet61cc8522020-04-20 14:54:42 +02003130 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3131 /* Add proxy environment variables */
3132 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3133 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3134 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3135 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3136 /* Add server environment variables */
3137 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3138 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3139 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3140 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3141 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3142 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003143
Christopher Faulet61cc8522020-04-20 14:54:42 +02003144 /* Ensure that we don't leave any hole in check->envp */
3145 for (i = 0; i < EXTCHK_SIZE; i++)
3146 if (!check->envp[i])
3147 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003148
Christopher Faulet61cc8522020-04-20 14:54:42 +02003149 return 1;
3150err:
3151 if (check->envp) {
3152 for (i = 0; i < EXTCHK_SIZE; i++)
3153 free(check->envp[i]);
3154 free(check->envp);
3155 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003156 }
3157
Christopher Faulet61cc8522020-04-20 14:54:42 +02003158 if (check->argv) {
3159 for (i = 1; i < 5; i++)
3160 free(check->argv[i]);
3161 free(check->argv);
3162 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003163 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003164 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003165}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003166
Christopher Faulet61cc8522020-04-20 14:54:42 +02003167/*
3168 * establish a server health-check that makes use of a process.
3169 *
3170 * It can return one of :
3171 * - SF_ERR_NONE if everything's OK
3172 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3173 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3174 *
3175 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003176 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003177static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003178{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003179 char buf[256];
3180 struct check *check = t->context;
3181 struct server *s = check->server;
3182 struct proxy *px = s->proxy;
3183 int status;
3184 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003185
Christopher Faulet61cc8522020-04-20 14:54:42 +02003186 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003187
Christopher Faulet61cc8522020-04-20 14:54:42 +02003188 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003189
Christopher Faulet61cc8522020-04-20 14:54:42 +02003190 pid = fork();
3191 if (pid < 0) {
3192 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3193 (global.tune.options & GTUNE_INSECURE_FORK) ?
3194 "" : " (likely caused by missing 'insecure-fork-wanted')",
3195 strerror(errno));
3196 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003197 goto out;
3198 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003199 if (pid == 0) {
3200 /* Child */
3201 extern char **environ;
3202 struct rlimit limit;
3203 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003204
Christopher Faulet61cc8522020-04-20 14:54:42 +02003205 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3206 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003207
Christopher Faulet61cc8522020-04-20 14:54:42 +02003208 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003209
Christopher Faulet61cc8522020-04-20 14:54:42 +02003210 /* restore the initial FD limits */
3211 limit.rlim_cur = rlim_fd_cur_at_boot;
3212 limit.rlim_max = rlim_fd_max_at_boot;
3213 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3214 getrlimit(RLIMIT_NOFILE, &limit);
3215 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3216 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3217 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3218 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003219
Christopher Faulet61cc8522020-04-20 14:54:42 +02003220 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003221
Christopher Faulet61cc8522020-04-20 14:54:42 +02003222 /* Update some environment variables and command args: curconn, server addr and server port */
3223 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003224
Christopher Faulet61cc8522020-04-20 14:54:42 +02003225 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3226 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003227
Christopher Faulet61cc8522020-04-20 14:54:42 +02003228 *check->argv[4] = 0;
3229 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3230 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3231 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003232
Christopher Faulet61cc8522020-04-20 14:54:42 +02003233 haproxy_unblock_signals();
3234 execvp(px->check_command, check->argv);
3235 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3236 strerror(errno));
3237 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003238 }
3239
Christopher Faulet61cc8522020-04-20 14:54:42 +02003240 /* Parent */
3241 if (check->result == CHK_RES_UNKNOWN) {
3242 if (pid_list_add(pid, t) != NULL) {
3243 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3244
3245 if (px->timeout.check && px->timeout.connect) {
3246 int t_con = tick_add(now_ms, px->timeout.connect);
3247 t->expire = tick_first(t->expire, t_con);
3248 }
3249 status = SF_ERR_NONE;
3250 goto out;
3251 }
3252 else {
3253 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3254 }
3255 kill(pid, SIGTERM); /* process creation error */
3256 }
3257 else
3258 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3259
3260out:
3261 unblock_sigchld();
3262 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003263}
3264
Christopher Faulet61cc8522020-04-20 14:54:42 +02003265/*
3266 * manages a server health-check that uses an external process. Returns
3267 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003268 *
3269 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003270 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003271 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003272static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003273{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003274 struct check *check = context;
3275 struct server *s = check->server;
3276 int rv;
3277 int ret;
3278 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003279
Christopher Faulet61cc8522020-04-20 14:54:42 +02003280 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3281 if (!(check->state & CHK_ST_INPROGRESS)) {
3282 /* no check currently running */
3283 if (!expired) /* woke up too early */
3284 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003285
Christopher Faulet61cc8522020-04-20 14:54:42 +02003286 /* we don't send any health-checks when the proxy is
3287 * stopped, the server should not be checked or the check
3288 * is disabled.
3289 */
3290 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3291 s->proxy->state == PR_STSTOPPED)
3292 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003293
Christopher Faulet61cc8522020-04-20 14:54:42 +02003294 /* we'll initiate a new check */
3295 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003296
Christopher Faulet61cc8522020-04-20 14:54:42 +02003297 check->state |= CHK_ST_INPROGRESS;
3298
3299 ret = connect_proc_chk(t);
3300 if (ret == SF_ERR_NONE) {
3301 /* the process was forked, we allow up to min(inter,
3302 * timeout.connect) for it to report its status, but
3303 * only when timeout.check is set as it may be to short
3304 * for a full check otherwise.
3305 */
3306 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3307
3308 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3309 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3310 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003311 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003312 task_set_affinity(t, tid_bit);
3313 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003314 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003315
Christopher Faulet61cc8522020-04-20 14:54:42 +02003316 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003317
Christopher Faulet61cc8522020-04-20 14:54:42 +02003318 check->state &= ~CHK_ST_INPROGRESS;
3319 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003320
Christopher Faulet61cc8522020-04-20 14:54:42 +02003321 /* we allow up to min(inter, timeout.connect) for a connection
3322 * to establish but only when timeout.check is set
3323 * as it may be to short for a full check otherwise
3324 */
3325 while (tick_is_expired(t->expire, now_ms)) {
3326 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003327
Christopher Faulet61cc8522020-04-20 14:54:42 +02003328 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3329 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003330
Christopher Faulet61cc8522020-04-20 14:54:42 +02003331 if (s->proxy->timeout.check)
3332 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003333 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003334 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003335 else {
3336 /* there was a test running.
3337 * First, let's check whether there was an uncaught error,
3338 * which can happen on connect timeout or error.
3339 */
3340 if (check->result == CHK_RES_UNKNOWN) {
3341 /* good connection is enough for pure TCP check */
3342 struct pid_list *elem = check->curpid;
3343 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003344
Christopher Faulet61cc8522020-04-20 14:54:42 +02003345 if (elem->exited) {
3346 status = elem->status; /* Save in case the process exits between use below */
3347 if (!WIFEXITED(status))
3348 check->code = -1;
3349 else
3350 check->code = WEXITSTATUS(status);
3351 if (!WIFEXITED(status) || WEXITSTATUS(status))
3352 status = HCHK_STATUS_PROCERR;
3353 else
3354 status = HCHK_STATUS_PROCOK;
3355 } else if (expired) {
3356 status = HCHK_STATUS_PROCTOUT;
3357 ha_warning("kill %d\n", (int)elem->pid);
3358 kill(elem->pid, SIGTERM);
3359 }
3360 set_server_check_status(check, status, NULL);
3361 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003362
Christopher Faulet61cc8522020-04-20 14:54:42 +02003363 if (check->result == CHK_RES_FAILED) {
3364 /* a failure or timeout detected */
3365 check_notify_failure(check);
3366 }
3367 else if (check->result == CHK_RES_CONDPASS) {
3368 /* check is OK but asks for stopping mode */
3369 check_notify_stopping(check);
3370 }
3371 else if (check->result == CHK_RES_PASSED) {
3372 /* a success was detected */
3373 check_notify_success(check);
3374 }
3375 task_set_affinity(t, 1);
3376 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003377
Christopher Faulet61cc8522020-04-20 14:54:42 +02003378 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003379
Christopher Faulet61cc8522020-04-20 14:54:42 +02003380 rv = 0;
3381 if (global.spread_checks > 0) {
3382 rv = srv_getinter(check) * global.spread_checks / 100;
3383 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3384 }
3385 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3386 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003387
Christopher Faulet61cc8522020-04-20 14:54:42 +02003388 reschedule:
3389 while (tick_is_expired(t->expire, now_ms))
3390 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003391
Christopher Faulet61cc8522020-04-20 14:54:42 +02003392 out_unlock:
3393 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3394 return t;
3395}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003396
Baptiste Assmann248f1172018-03-01 21:49:01 +01003397
Christopher Faulet61cc8522020-04-20 14:54:42 +02003398/**************************************************************************/
3399/***************** Health-checks based on connections *********************/
3400/**************************************************************************/
3401/* This function is used only for server health-checks. It handles connection
3402 * status updates including errors. If necessary, it wakes the check task up.
3403 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3404 * connection (eg: reconnect). It relies on tcpcheck_main().
3405 */
3406static int wake_srv_chk(struct conn_stream *cs)
3407{
3408 struct connection *conn = cs->conn;
3409 struct check *check = cs->data;
3410 struct email_alertq *q = container_of(check, typeof(*q), check);
3411 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003412
Christopher Faulet61cc8522020-04-20 14:54:42 +02003413 if (check->server)
3414 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3415 else
3416 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003417
Christopher Faulet61cc8522020-04-20 14:54:42 +02003418 /* we may have to make progress on the TCP checks */
3419 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003420
Christopher Faulet61cc8522020-04-20 14:54:42 +02003421 cs = check->cs;
3422 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003423
Christopher Faulet61cc8522020-04-20 14:54:42 +02003424 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3425 /* We may get error reports bypassing the I/O handlers, typically
3426 * the case when sending a pure TCP check which fails, then the I/O
3427 * handlers above are not called. This is completely handled by the
3428 * main processing task so let's simply wake it up. If we get here,
3429 * we expect errno to still be valid.
3430 */
3431 chk_report_conn_err(check, errno, 0);
3432 task_wakeup(check->task, TASK_WOKEN_IO);
3433 }
3434
3435 if (check->result != CHK_RES_UNKNOWN) {
3436 /* Check complete or aborted. If connection not yet closed do it
3437 * now and wake the check task up to be sure the result is
3438 * handled ASAP. */
3439 conn_sock_drain(conn);
3440 cs_close(cs);
3441 ret = -1;
3442 /* We may have been scheduled to run, and the
3443 * I/O handler expects to have a cs, so remove
3444 * the tasklet
3445 */
3446 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3447 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003448 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003449
3450 if (check->server)
3451 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003452 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003453 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003454
Christopher Faulet61cc8522020-04-20 14:54:42 +02003455 /* if a connection got replaced, we must absolutely prevent the connection
3456 * handler from touching its fd, and perform the FD polling updates ourselves
3457 */
3458 if (ret < 0)
3459 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003460
Christopher Faulet61cc8522020-04-20 14:54:42 +02003461 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003462}
3463
Christopher Faulet61cc8522020-04-20 14:54:42 +02003464/* This function checks if any I/O is wanted, and if so, attempts to do so */
3465static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003466{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003467 struct check *check = ctx;
3468 struct conn_stream *cs = check->cs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003469
Christopher Faulet3d5e1212020-05-28 14:34:02 +02003470 wake_srv_chk(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003471 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003472}
3473
Christopher Faulet61cc8522020-04-20 14:54:42 +02003474/* manages a server health-check that uses a connection. Returns
3475 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3476 *
3477 * Please do NOT place any return statement in this function and only leave
3478 * via the out_unlock label.
3479 */
3480static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003481{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003482 struct check *check = context;
3483 struct proxy *proxy = check->proxy;
3484 struct conn_stream *cs = check->cs;
3485 struct connection *conn = cs_conn(cs);
3486 int rv;
3487 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003488
Christopher Faulet61cc8522020-04-20 14:54:42 +02003489 if (check->server)
3490 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3491 if (!(check->state & CHK_ST_INPROGRESS)) {
3492 /* no check currently running */
3493 if (!expired) /* woke up too early */
3494 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003495
Christopher Faulet61cc8522020-04-20 14:54:42 +02003496 /* we don't send any health-checks when the proxy is
3497 * stopped, the server should not be checked or the check
3498 * is disabled.
3499 */
3500 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3501 proxy->state == PR_STSTOPPED)
3502 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003503
Christopher Faulet61cc8522020-04-20 14:54:42 +02003504 /* we'll initiate a new check */
3505 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003506
Christopher Faulet61cc8522020-04-20 14:54:42 +02003507 check->state |= CHK_ST_INPROGRESS;
3508 b_reset(&check->bi);
3509 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003510
Christopher Faulet61cc8522020-04-20 14:54:42 +02003511 task_set_affinity(t, tid_bit);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003512
Christopher Faulet99ff1052020-05-25 07:32:01 +02003513 check->current_step = NULL;
3514 tcpcheck_main(check);
3515 goto out_unlock;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003516 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003517 else {
3518 /* there was a test running.
3519 * First, let's check whether there was an uncaught error,
3520 * which can happen on connect timeout or error.
3521 */
3522 if (check->result == CHK_RES_UNKNOWN) {
3523 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3524 chk_report_conn_err(check, 0, expired);
3525 }
3526 else
3527 goto out_unlock; /* timeout not reached, wait again */
3528 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003529
Christopher Faulet61cc8522020-04-20 14:54:42 +02003530 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003531
Christopher Faulet61cc8522020-04-20 14:54:42 +02003532 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003533
Christopher Faulet61cc8522020-04-20 14:54:42 +02003534 if (conn && conn->xprt) {
3535 /* The check was aborted and the connection was not yet closed.
3536 * This can happen upon timeout, or when an external event such
3537 * as a failed response coupled with "observe layer7" caused the
3538 * server state to be suddenly changed.
3539 */
3540 conn_sock_drain(conn);
3541 cs_close(cs);
3542 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003543
Christopher Faulet61cc8522020-04-20 14:54:42 +02003544 if (cs) {
3545 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003546 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003547 /* We may have been scheduled to run, and the
3548 * I/O handler expects to have a cs, so remove
3549 * the tasklet
3550 */
3551 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3552 cs_destroy(cs);
3553 cs = check->cs = NULL;
3554 conn = NULL;
3555 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003556
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003557 if (check->sess != NULL) {
3558 vars_prune(&check->vars, check->sess, NULL);
3559 session_free(check->sess);
3560 check->sess = NULL;
3561 }
3562
Christopher Faulet61cc8522020-04-20 14:54:42 +02003563 if (check->server) {
3564 if (check->result == CHK_RES_FAILED) {
3565 /* a failure or timeout detected */
3566 check_notify_failure(check);
3567 }
3568 else if (check->result == CHK_RES_CONDPASS) {
3569 /* check is OK but asks for stopping mode */
3570 check_notify_stopping(check);
3571 }
3572 else if (check->result == CHK_RES_PASSED) {
3573 /* a success was detected */
3574 check_notify_success(check);
3575 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003576 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003577 task_set_affinity(t, MAX_THREADS_MASK);
3578 check->state &= ~CHK_ST_INPROGRESS;
3579
3580 if (check->server) {
3581 rv = 0;
3582 if (global.spread_checks > 0) {
3583 rv = srv_getinter(check) * global.spread_checks / 100;
3584 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3585 }
3586 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003587 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003588 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003589
Christopher Faulet61cc8522020-04-20 14:54:42 +02003590 reschedule:
3591 while (tick_is_expired(t->expire, now_ms))
3592 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3593 out_unlock:
3594 if (check->server)
3595 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3596 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003597}
3598
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003599
Christopher Faulet61cc8522020-04-20 14:54:42 +02003600/**************************************************************************/
3601/******************* Internals to parse tcp-check rules *******************/
3602/**************************************************************************/
3603struct action_kw_list tcp_check_keywords = {
3604 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3605};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003606
Christopher Faulet61cc8522020-04-20 14:54:42 +02003607/* Return the struct action_kw associated to a keyword */
3608static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003609{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003610 return action_lookup(&tcp_check_keywords.list, kw);
3611}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003612
Christopher Faulet61cc8522020-04-20 14:54:42 +02003613static void action_kw_tcp_check_build_list(struct buffer *chk)
3614{
3615 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003616}
3617
Christopher Faulet61cc8522020-04-20 14:54:42 +02003618/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3619 * returned on error.
3620 */
3621static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3622 struct list *rules, struct action_kw *kw,
3623 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003624{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003625 struct tcpcheck_rule *chk = NULL;
3626 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003627
Christopher Faulet61cc8522020-04-20 14:54:42 +02003628 actrule = calloc(1, sizeof(*actrule));
3629 if (!actrule) {
3630 memprintf(errmsg, "out of memory");
3631 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003632 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003633 actrule->kw = kw;
3634 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003635
Christopher Faulet61cc8522020-04-20 14:54:42 +02003636 cur_arg++;
3637 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3638 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3639 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003640 }
3641
Christopher Faulet61cc8522020-04-20 14:54:42 +02003642 chk = calloc(1, sizeof(*chk));
3643 if (!chk) {
3644 memprintf(errmsg, "out of memory");
3645 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003646 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003647 chk->action = TCPCHK_ACT_ACTION_KW;
3648 chk->action_kw.rule = actrule;
3649 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003650
3651 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003652 free(actrule);
3653 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003654}
3655
Christopher Faulet61cc8522020-04-20 14:54:42 +02003656/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3657 * returned on error.
3658 */
3659static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3660 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003661{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003662 struct tcpcheck_rule *chk = NULL;
3663 struct sockaddr_storage *sk = NULL;
3664 char *comment = NULL, *sni = NULL, *alpn = NULL;
3665 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003666 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003667 unsigned short conn_opts = 0;
3668 long port = 0;
3669 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003670
Christopher Faulet61cc8522020-04-20 14:54:42 +02003671 list_for_each_entry(chk, rules, list) {
3672 if (chk->action == TCPCHK_ACT_CONNECT)
3673 break;
3674 if (chk->action == TCPCHK_ACT_COMMENT ||
3675 chk->action == TCPCHK_ACT_ACTION_KW ||
3676 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3677 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003678
Christopher Faulet61cc8522020-04-20 14:54:42 +02003679 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003680 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003681 "when there is a 'connect' step in the tcp-check ruleset");
3682 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003683 }
3684
Christopher Faulet61cc8522020-04-20 14:54:42 +02003685 cur_arg++;
3686 while (*(args[cur_arg])) {
3687 if (strcmp(args[cur_arg], "default") == 0)
3688 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3689 else if (strcmp(args[cur_arg], "addr") == 0) {
3690 int port1, port2;
3691 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003692
Christopher Faulet61cc8522020-04-20 14:54:42 +02003693 if (!*(args[cur_arg+1])) {
3694 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3695 goto error;
3696 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003697
Christopher Faulet61cc8522020-04-20 14:54:42 +02003698 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3699 if (!sk) {
3700 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3701 goto error;
3702 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003703
Christopher Faulet61cc8522020-04-20 14:54:42 +02003704 proto = protocol_by_family(sk->ss_family);
3705 if (!proto || !proto->connect) {
3706 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3707 args[cur_arg]);
3708 goto error;
3709 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003710
Christopher Faulet61cc8522020-04-20 14:54:42 +02003711 if (port1 != port2) {
3712 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3713 args[cur_arg], args[cur_arg+1]);
3714 goto error;
3715 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003716
Christopher Faulet61cc8522020-04-20 14:54:42 +02003717 cur_arg++;
3718 }
3719 else if (strcmp(args[cur_arg], "port") == 0) {
3720 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003721
Christopher Faulet61cc8522020-04-20 14:54:42 +02003722 if (!*(args[cur_arg+1])) {
3723 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3724 goto error;
3725 }
3726 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003727
Christopher Faulet61cc8522020-04-20 14:54:42 +02003728 port = 0;
3729 release_sample_expr(port_expr);
3730 p = args[cur_arg]; end = p + strlen(p);
3731 port = read_uint(&p, end);
3732 if (p != end) {
3733 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003734
Christopher Faulet61cc8522020-04-20 14:54:42 +02003735 px->conf.args.ctx = ARGC_SRV;
3736 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3737 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003738
Christopher Faulet61cc8522020-04-20 14:54:42 +02003739 if (!port_expr) {
3740 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3741 goto error;
3742 }
3743 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3744 memprintf(errmsg, "error detected while parsing port expression : "
3745 " fetch method '%s' extracts information from '%s', "
3746 "none of which is available here.\n",
3747 args[cur_arg], sample_src_names(port_expr->fetch->use));
3748 goto error;
3749 }
3750 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3751 }
3752 else if (port > 65535 || port < 1) {
3753 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3754 args[cur_arg]);
3755 goto error;
3756 }
3757 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003758 else if (strcmp(args[cur_arg], "proto") == 0) {
3759 if (!*(args[cur_arg+1])) {
3760 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3761 goto error;
3762 }
3763 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3764 if (!mux_proto) {
3765 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3766 goto error;
3767 }
3768 cur_arg++;
3769 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003770 else if (strcmp(args[cur_arg], "comment") == 0) {
3771 if (!*(args[cur_arg+1])) {
3772 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3773 goto error;
3774 }
3775 cur_arg++;
3776 free(comment);
3777 comment = strdup(args[cur_arg]);
3778 if (!comment) {
3779 memprintf(errmsg, "out of memory");
3780 goto error;
3781 }
3782 }
3783 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3784 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3785 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3786 conn_opts |= TCPCHK_OPT_SOCKS4;
3787 else if (strcmp(args[cur_arg], "linger") == 0)
3788 conn_opts |= TCPCHK_OPT_LINGER;
3789#ifdef USE_OPENSSL
3790 else if (strcmp(args[cur_arg], "ssl") == 0) {
3791 px->options |= PR_O_TCPCHK_SSL;
3792 conn_opts |= TCPCHK_OPT_SSL;
3793 }
3794 else if (strcmp(args[cur_arg], "sni") == 0) {
3795 if (!*(args[cur_arg+1])) {
3796 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3797 goto error;
3798 }
3799 cur_arg++;
3800 free(sni);
3801 sni = strdup(args[cur_arg]);
3802 if (!sni) {
3803 memprintf(errmsg, "out of memory");
3804 goto error;
3805 }
3806 }
3807 else if (strcmp(args[cur_arg], "alpn") == 0) {
3808#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3809 free(alpn);
3810 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3811 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3812 goto error;
3813 }
3814 cur_arg++;
3815#else
3816 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003817 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003818#endif
3819 }
3820#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003821
Christopher Faulet61cc8522020-04-20 14:54:42 +02003822 else {
3823 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3824#ifdef USE_OPENSSL
3825 ", 'ssl', 'sni', 'alpn'"
3826#endif /* USE_OPENSSL */
3827 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3828 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003829 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003830 }
3831 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003832 }
3833
Christopher Faulet61cc8522020-04-20 14:54:42 +02003834 chk = calloc(1, sizeof(*chk));
3835 if (!chk) {
3836 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003837 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003838 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003839 chk->action = TCPCHK_ACT_CONNECT;
3840 chk->comment = comment;
3841 chk->connect.port = port;
3842 chk->connect.options = conn_opts;
3843 chk->connect.sni = sni;
3844 chk->connect.alpn = alpn;
3845 chk->connect.alpn_len= alpn_len;
3846 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003847 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003848 if (sk)
3849 chk->connect.addr = *sk;
3850 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003851
Christopher Faulet61cc8522020-04-20 14:54:42 +02003852 error:
3853 free(alpn);
3854 free(sni);
3855 free(comment);
3856 release_sample_expr(port_expr);
3857 return NULL;
3858}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003859
Christopher Faulet61cc8522020-04-20 14:54:42 +02003860/* Parses and creates a tcp-check send rule. NULL is returned on error */
3861static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3862 const char *file, int line, char **errmsg)
3863{
3864 struct tcpcheck_rule *chk = NULL;
3865 char *comment = NULL, *data = NULL;
3866 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003867
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003868 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3869 type = TCPCHK_SEND_BINARY_LF;
3870 else if (strcmp(args[cur_arg], "send-binary") == 0)
3871 type = TCPCHK_SEND_BINARY;
3872 else if (strcmp(args[cur_arg], "send-lf") == 0)
3873 type = TCPCHK_SEND_STRING_LF;
3874 else if (strcmp(args[cur_arg], "send") == 0)
3875 type = TCPCHK_SEND_STRING;
3876
Christopher Faulet61cc8522020-04-20 14:54:42 +02003877 if (!*(args[cur_arg+1])) {
3878 memprintf(errmsg, "'%s' expects a %s as argument",
3879 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003880 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003881 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003882
Christopher Faulet61cc8522020-04-20 14:54:42 +02003883 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003884
Christopher Faulet61cc8522020-04-20 14:54:42 +02003885 cur_arg += 2;
3886 while (*(args[cur_arg])) {
3887 if (strcmp(args[cur_arg], "comment") == 0) {
3888 if (!*(args[cur_arg+1])) {
3889 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3890 goto error;
3891 }
3892 cur_arg++;
3893 free(comment);
3894 comment = strdup(args[cur_arg]);
3895 if (!comment) {
3896 memprintf(errmsg, "out of memory");
3897 goto error;
3898 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003899 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003900 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003901 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003902 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003903 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003904 }
3905 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003906 }
3907
Christopher Faulet61cc8522020-04-20 14:54:42 +02003908 chk = calloc(1, sizeof(*chk));
3909 if (!chk) {
3910 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003911 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003912 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003913 chk->action = TCPCHK_ACT_SEND;
3914 chk->comment = comment;
3915 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003916
Christopher Faulet61cc8522020-04-20 14:54:42 +02003917 switch (chk->send.type) {
3918 case TCPCHK_SEND_STRING:
3919 chk->send.data = ist2(strdup(data), strlen(data));
3920 if (!isttest(chk->send.data)) {
3921 memprintf(errmsg, "out of memory");
3922 goto error;
3923 }
3924 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003925 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003926 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003927 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003928 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3929 goto error;
3930 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003931 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003932 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003933 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003934 case TCPCHK_SEND_STRING_LF:
3935 case TCPCHK_SEND_BINARY_LF:
3936 LIST_INIT(&chk->send.fmt);
3937 px->conf.args.ctx = ARGC_SRV;
3938 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3939 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3940 goto error;
3941 }
3942 break;
3943 case TCPCHK_SEND_HTTP:
3944 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003945 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003946 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003947
Christopher Faulet61cc8522020-04-20 14:54:42 +02003948 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003949
Christopher Faulet61cc8522020-04-20 14:54:42 +02003950 error:
3951 free(chk);
3952 free(comment);
3953 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003954}
3955
Christopher Faulet61cc8522020-04-20 14:54:42 +02003956/* Parses and creates a http-check send rule. NULL is returned on error */
3957static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3958 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003959{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003960 struct tcpcheck_rule *chk = NULL;
3961 struct tcpcheck_http_hdr *hdr = NULL;
3962 struct http_hdr hdrs[global.tune.max_http_hdr];
3963 char *meth = NULL, *uri = NULL, *vsn = NULL;
3964 char *body = NULL, *comment = NULL;
3965 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003966 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003967
3968 cur_arg++;
3969 while (*(args[cur_arg])) {
3970 if (strcmp(args[cur_arg], "meth") == 0) {
3971 if (!*(args[cur_arg+1])) {
3972 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3973 goto error;
3974 }
3975 cur_arg++;
3976 meth = args[cur_arg];
3977 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003978 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003979 if (!*(args[cur_arg+1])) {
3980 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3981 goto error;
3982 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003983 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
3984 if (strcmp(args[cur_arg], "uri-lf") == 0)
3985 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003986 cur_arg++;
3987 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02003988 }
Christopher Faulet907701b2020-04-28 09:37:00 +02003989 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003990 if (!*(args[cur_arg+1])) {
3991 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3992 goto error;
3993 }
3994 cur_arg++;
3995 vsn = args[cur_arg];
3996 }
3997 else if (strcmp(args[cur_arg], "hdr") == 0) {
3998 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
3999 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4000 goto error;
4001 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004002
4003 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4004 if (host_hdr >= 0) {
4005 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4006 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4007 goto error;
4008 }
4009 host_hdr = i;
4010 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004011 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4012 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4013 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4014 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004015
Christopher Faulet61cc8522020-04-20 14:54:42 +02004016 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4017 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4018 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004019 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004020 cur_arg += 2;
4021 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004022 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004023 if (!*(args[cur_arg+1])) {
4024 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4025 goto error;
4026 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004027 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4028 if (strcmp(args[cur_arg], "body-lf") == 0)
4029 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004030 cur_arg++;
4031 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004032 }
4033 else if (strcmp(args[cur_arg], "comment") == 0) {
4034 if (!*(args[cur_arg+1])) {
4035 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4036 goto error;
4037 }
4038 cur_arg++;
4039 free(comment);
4040 comment = strdup(args[cur_arg]);
4041 if (!comment) {
4042 memprintf(errmsg, "out of memory");
4043 goto error;
4044 }
4045 }
4046 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004047 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004048 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004049 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004050 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004051 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004052 }
4053
Christopher Faulet61cc8522020-04-20 14:54:42 +02004054 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004055
Christopher Faulet61cc8522020-04-20 14:54:42 +02004056 chk = calloc(1, sizeof(*chk));
4057 if (!chk) {
4058 memprintf(errmsg, "out of memory");
4059 goto error;
4060 }
4061 chk->action = TCPCHK_ACT_SEND;
4062 chk->comment = comment; comment = NULL;
4063 chk->send.type = TCPCHK_SEND_HTTP;
4064 chk->send.http.flags = flags;
4065 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004066
Christopher Faulet61cc8522020-04-20 14:54:42 +02004067 if (meth) {
4068 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4069 chk->send.http.meth.str.area = strdup(meth);
4070 chk->send.http.meth.str.data = strlen(meth);
4071 if (!chk->send.http.meth.str.area) {
4072 memprintf(errmsg, "out of memory");
4073 goto error;
4074 }
4075 }
4076 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004077 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4078 LIST_INIT(&chk->send.http.uri_fmt);
4079 px->conf.args.ctx = ARGC_SRV;
4080 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4081 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4082 goto error;
4083 }
4084 }
4085 else {
4086 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4087 if (!isttest(chk->send.http.uri)) {
4088 memprintf(errmsg, "out of memory");
4089 goto error;
4090 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004091 }
4092 }
4093 if (vsn) {
4094 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4095 if (!isttest(chk->send.http.vsn)) {
4096 memprintf(errmsg, "out of memory");
4097 goto error;
4098 }
4099 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004100 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004101 hdr = calloc(1, sizeof(*hdr));
4102 if (!hdr) {
4103 memprintf(errmsg, "out of memory");
4104 goto error;
4105 }
4106 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004107 hdr->name = istdup(hdrs[i].n);
4108 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004109 memprintf(errmsg, "out of memory");
4110 goto error;
4111 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004112
Christopher Fauletb61caf42020-04-21 10:57:42 +02004113 ist0(hdrs[i].v);
4114 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 +02004115 goto error;
4116 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4117 hdr = NULL;
4118 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004119
Christopher Faulet61cc8522020-04-20 14:54:42 +02004120 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004121 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4122 LIST_INIT(&chk->send.http.body_fmt);
4123 px->conf.args.ctx = ARGC_SRV;
4124 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4125 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4126 goto error;
4127 }
4128 }
4129 else {
4130 chk->send.http.body = ist2(strdup(body), strlen(body));
4131 if (!isttest(chk->send.http.body)) {
4132 memprintf(errmsg, "out of memory");
4133 goto error;
4134 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004135 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004136 }
4137
Christopher Faulet61cc8522020-04-20 14:54:42 +02004138 return chk;
4139
4140 error:
4141 free_tcpcheck_http_hdr(hdr);
4142 free_tcpcheck(chk, 0);
4143 free(comment);
4144 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004145}
4146
Christopher Faulet61cc8522020-04-20 14:54:42 +02004147/* Parses and creates a http-check comment rule. NULL is returned on error */
4148static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4149 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004150{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004151 struct tcpcheck_rule *chk = NULL;
4152 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004153
Christopher Faulet61cc8522020-04-20 14:54:42 +02004154 if (!*(args[cur_arg+1])) {
4155 memprintf(errmsg, "expects a string as argument");
4156 goto error;
4157 }
4158 cur_arg++;
4159 comment = strdup(args[cur_arg]);
4160 if (!comment) {
4161 memprintf(errmsg, "out of memory");
4162 goto error;
4163 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004164
Christopher Faulet61cc8522020-04-20 14:54:42 +02004165 chk = calloc(1, sizeof(*chk));
4166 if (!chk) {
4167 memprintf(errmsg, "out of memory");
4168 goto error;
4169 }
4170 chk->action = TCPCHK_ACT_COMMENT;
4171 chk->comment = comment;
4172 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004173
Christopher Faulet61cc8522020-04-20 14:54:42 +02004174 error:
4175 free(comment);
4176 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004177}
4178
Christopher Faulet61cc8522020-04-20 14:54:42 +02004179/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4180 * on error. <proto> is set to the right protocol flags (covered by the
4181 * TCPCHK_RULES_PROTO_CHK mask).
4182 */
4183static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4184 struct list *rules, unsigned int proto,
4185 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004186{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004187 struct tcpcheck_rule *prev_check, *chk = NULL;
4188 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004189 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004190 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004191 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4192 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4193 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004194 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004195 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004196 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004197
Christopher Faulet39708192020-05-05 10:47:36 +02004198 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004199 if (!*(args[cur_arg+1])) {
4200 memprintf(errmsg, "expects at least a matching pattern as arguments");
4201 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004202 }
4203
Christopher Faulet61cc8522020-04-20 14:54:42 +02004204 cur_arg++;
4205 while (*(args[cur_arg])) {
4206 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004207
Christopher Faulet61cc8522020-04-20 14:54:42 +02004208 rescan:
4209 if (strcmp(args[cur_arg], "min-recv") == 0) {
4210 if (in_pattern) {
4211 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4212 goto error;
4213 }
4214 if (!*(args[cur_arg+1])) {
4215 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4216 goto error;
4217 }
4218 /* Use an signed integer here because of chksize */
4219 cur_arg++;
4220 min_recv = atol(args[cur_arg]);
4221 if (min_recv < -1 || min_recv > INT_MAX) {
4222 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4223 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004224 }
4225 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004226 else if (*(args[cur_arg]) == '!') {
4227 in_pattern = 1;
4228 while (*(args[cur_arg]) == '!') {
4229 inverse = !inverse;
4230 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004231 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004232 if (!*(args[cur_arg]))
4233 cur_arg++;
4234 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004235 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004236 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4237 if (type != TCPCHK_EXPECT_UNDEF) {
4238 memprintf(errmsg, "only on pattern expected");
4239 goto error;
4240 }
4241 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004242 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004243 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004244 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004245
Christopher Faulet61cc8522020-04-20 14:54:42 +02004246 if (!*(args[cur_arg+1])) {
4247 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4248 goto error;
4249 }
4250 cur_arg++;
4251 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004252 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004253 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4254 if (proto == TCPCHK_RULES_HTTP_CHK)
4255 goto bad_http_kw;
4256 if (type != TCPCHK_EXPECT_UNDEF) {
4257 memprintf(errmsg, "only on pattern expected");
4258 goto error;
4259 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004260 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004261
Christopher Faulet61cc8522020-04-20 14:54:42 +02004262 if (!*(args[cur_arg+1])) {
4263 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4264 goto error;
4265 }
4266 cur_arg++;
4267 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004268 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004269 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4270 if (type != TCPCHK_EXPECT_UNDEF) {
4271 memprintf(errmsg, "only on pattern expected");
4272 goto error;
4273 }
4274 if (proto != TCPCHK_RULES_HTTP_CHK)
4275 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4276 else {
4277 if (*(args[cur_arg]) != 's')
4278 goto bad_http_kw;
4279 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4280 }
4281
4282 if (!*(args[cur_arg+1])) {
4283 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4284 goto error;
4285 }
4286 cur_arg++;
4287 pattern = args[cur_arg];
4288 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004289 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4290 if (proto != TCPCHK_RULES_HTTP_CHK)
4291 goto bad_tcp_kw;
4292 if (type != TCPCHK_EXPECT_UNDEF) {
4293 memprintf(errmsg, "only on pattern expected");
4294 goto error;
4295 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004296 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004297
Christopher Faulet61cc8522020-04-20 14:54:42 +02004298 if (!*(args[cur_arg+1])) {
4299 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4300 goto error;
4301 }
4302 cur_arg++;
4303 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004304 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004305 else if (strcmp(args[cur_arg], "custom") == 0) {
4306 if (in_pattern) {
4307 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4308 goto error;
4309 }
4310 if (type != TCPCHK_EXPECT_UNDEF) {
4311 memprintf(errmsg, "only on pattern expected");
4312 goto error;
4313 }
4314 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004315 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004316 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004317 int orig_arg = cur_arg;
4318
4319 if (proto != TCPCHK_RULES_HTTP_CHK)
4320 goto bad_tcp_kw;
4321 if (type != TCPCHK_EXPECT_UNDEF) {
4322 memprintf(errmsg, "only on pattern expected");
4323 goto error;
4324 }
4325 type = TCPCHK_EXPECT_HTTP_HEADER;
4326
Christopher Fauletb5594262020-05-05 20:23:13 +02004327 if (strcmp(args[cur_arg], "fhdr") == 0)
4328 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4329
Christopher Faulet39708192020-05-05 10:47:36 +02004330 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004331 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4332 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4333 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004334 args[orig_arg]);
4335 goto error;
4336 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004337
4338 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4339 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4340
Christopher Faulet39708192020-05-05 10:47:36 +02004341 cur_arg += 2;
4342 if (strcmp(args[cur_arg], "-m") == 0) {
4343 if (!*(args[cur_arg+1])) {
4344 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4345 args[orig_arg], args[cur_arg]);
4346 goto error;
4347 }
4348 if (strcmp(args[cur_arg+1], "str") == 0)
4349 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4350 else if (strcmp(args[cur_arg+1], "beg") == 0)
4351 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4352 else if (strcmp(args[cur_arg+1], "end") == 0)
4353 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4354 else if (strcmp(args[cur_arg+1], "sub") == 0)
4355 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004356 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4357 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4358 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4359 args[orig_arg]);
4360 goto error;
4361 }
Christopher Faulet39708192020-05-05 10:47:36 +02004362 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004363 }
Christopher Faulet39708192020-05-05 10:47:36 +02004364 else {
4365 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4366 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4367 goto error;
4368 }
4369 cur_arg += 2;
4370 }
4371 else
4372 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4373 npat = args[cur_arg];
4374
Christopher Fauletb5594262020-05-05 20:23:13 +02004375 if (!*(args[cur_arg+1]) ||
4376 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004377 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4378 goto next;
4379 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004380 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4381 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004382
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004383 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004384 if (strcmp(args[cur_arg+2], "-m") == 0) {
4385 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004386 if (!*(args[cur_arg+1])) {
4387 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4388 args[orig_arg], args[cur_arg]);
4389 goto error;
4390 }
4391 if (strcmp(args[cur_arg+1], "str") == 0)
4392 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4393 else if (strcmp(args[cur_arg+1], "beg") == 0)
4394 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4395 else if (strcmp(args[cur_arg+1], "end") == 0)
4396 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4397 else if (strcmp(args[cur_arg+1], "sub") == 0)
4398 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004399 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4400 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4401 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4402 args[orig_arg]);
4403 goto error;
4404 }
Christopher Faulet39708192020-05-05 10:47:36 +02004405 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004406 }
Christopher Faulet39708192020-05-05 10:47:36 +02004407 else {
4408 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4409 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4410 goto error;
4411 }
Christopher Faulet39708192020-05-05 10:47:36 +02004412 }
4413 else
4414 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004415
Christopher Fauletb5594262020-05-05 20:23:13 +02004416 if (!*(args[cur_arg+2])) {
4417 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4418 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004419 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004420 vpat = args[cur_arg+2];
4421 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004422 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004423 else if (strcmp(args[cur_arg], "comment") == 0) {
4424 if (in_pattern) {
4425 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4426 goto error;
4427 }
4428 if (!*(args[cur_arg+1])) {
4429 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4430 goto error;
4431 }
4432 cur_arg++;
4433 free(comment);
4434 comment = strdup(args[cur_arg]);
4435 if (!comment) {
4436 memprintf(errmsg, "out of memory");
4437 goto error;
4438 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004439 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004440 else if (strcmp(args[cur_arg], "on-success") == 0) {
4441 if (in_pattern) {
4442 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4443 goto error;
4444 }
4445 if (!*(args[cur_arg+1])) {
4446 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4447 goto error;
4448 }
4449 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004450 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004451 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004452 else if (strcmp(args[cur_arg], "on-error") == 0) {
4453 if (in_pattern) {
4454 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4455 goto error;
4456 }
4457 if (!*(args[cur_arg+1])) {
4458 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4459 goto error;
4460 }
4461 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004462 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004463 }
4464 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4465 if (in_pattern) {
4466 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4467 goto error;
4468 }
4469 if (!*(args[cur_arg+1])) {
4470 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4471 goto error;
4472 }
4473 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4474 ok_st = HCHK_STATUS_L7OKD;
4475 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4476 ok_st = HCHK_STATUS_L7OKCD;
4477 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4478 ok_st = HCHK_STATUS_L6OK;
4479 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4480 ok_st = HCHK_STATUS_L4OK;
4481 else {
4482 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4483 args[cur_arg], args[cur_arg+1]);
4484 goto error;
4485 }
4486 cur_arg++;
4487 }
4488 else if (strcmp(args[cur_arg], "error-status") == 0) {
4489 if (in_pattern) {
4490 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4491 goto error;
4492 }
4493 if (!*(args[cur_arg+1])) {
4494 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4495 goto error;
4496 }
4497 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4498 err_st = HCHK_STATUS_L7RSP;
4499 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4500 err_st = HCHK_STATUS_L7STS;
4501 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4502 err_st = HCHK_STATUS_L6RSP;
4503 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4504 err_st = HCHK_STATUS_L4CON;
4505 else {
4506 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4507 args[cur_arg], args[cur_arg+1]);
4508 goto error;
4509 }
4510 cur_arg++;
4511 }
4512 else if (strcmp(args[cur_arg], "status-code") == 0) {
4513 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004514
Christopher Faulet61cc8522020-04-20 14:54:42 +02004515 if (in_pattern) {
4516 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4517 goto error;
4518 }
4519 if (!*(args[cur_arg+1])) {
4520 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4521 goto error;
4522 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004523
Christopher Faulet61cc8522020-04-20 14:54:42 +02004524 cur_arg++;
4525 release_sample_expr(status_expr);
4526 px->conf.args.ctx = ARGC_SRV;
4527 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4528 file, line, errmsg, &px->conf.args, NULL);
4529 if (!status_expr) {
4530 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4531 goto error;
4532 }
4533 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4534 memprintf(errmsg, "error detected while parsing status-code expression : "
4535 " fetch method '%s' extracts information from '%s', "
4536 "none of which is available here.\n",
4537 args[cur_arg], sample_src_names(status_expr->fetch->use));
4538 goto error;
4539 }
4540 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4541 }
4542 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4543 if (in_pattern) {
4544 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4545 goto error;
4546 }
4547 if (!*(args[cur_arg+1])) {
4548 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4549 goto error;
4550 }
4551 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4552 tout_st = HCHK_STATUS_L7TOUT;
4553 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4554 tout_st = HCHK_STATUS_L6TOUT;
4555 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4556 tout_st = HCHK_STATUS_L4TOUT;
4557 else {
4558 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4559 args[cur_arg], args[cur_arg+1]);
4560 goto error;
4561 }
4562 cur_arg++;
4563 }
4564 else {
4565 if (proto == TCPCHK_RULES_HTTP_CHK) {
4566 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004567 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004568 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004569 }
4570 else {
4571 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004572 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4573 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004574 }
4575 goto error;
4576 }
Christopher Faulet39708192020-05-05 10:47:36 +02004577 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004578 cur_arg++;
4579 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004580
Christopher Faulet61cc8522020-04-20 14:54:42 +02004581 chk = calloc(1, sizeof(*chk));
4582 if (!chk) {
4583 memprintf(errmsg, "out of memory");
4584 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004585 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004586 chk->action = TCPCHK_ACT_EXPECT;
4587 LIST_INIT(&chk->expect.onerror_fmt);
4588 LIST_INIT(&chk->expect.onsuccess_fmt);
4589 chk->comment = comment; comment = NULL;
4590 chk->expect.type = type;
4591 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004592 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004593 chk->expect.ok_status = ok_st;
4594 chk->expect.err_status = err_st;
4595 chk->expect.tout_status = tout_st;
4596 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004597
Christopher Faulet61cc8522020-04-20 14:54:42 +02004598 if (on_success_msg) {
4599 px->conf.args.ctx = ARGC_SRV;
4600 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4601 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4602 goto error;
4603 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004604 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004605 if (on_error_msg) {
4606 px->conf.args.ctx = ARGC_SRV;
4607 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4608 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4609 goto error;
4610 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004611 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004612
Christopher Faulet61cc8522020-04-20 14:54:42 +02004613 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004614 case TCPCHK_EXPECT_HTTP_STATUS: {
4615 const char *p = pattern;
4616 unsigned int c1,c2;
4617
4618 chk->expect.codes.codes = NULL;
4619 chk->expect.codes.num = 0;
4620 while (1) {
4621 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4622 if (*p == '-') {
4623 p++;
4624 c2 = read_uint(&p, pattern + strlen(pattern));
4625 }
4626 if (c1 > c2) {
4627 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4628 goto error;
4629 }
4630
4631 chk->expect.codes.num++;
4632 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4633 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4634 if (!chk->expect.codes.codes) {
4635 memprintf(errmsg, "out of memory");
4636 goto error;
4637 }
4638 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4639 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4640
4641 if (*p == '\0')
4642 break;
4643 if (*p != ',') {
4644 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4645 goto error;
4646 }
4647 p++;
4648 }
4649 break;
4650 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004651 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004652 case TCPCHK_EXPECT_HTTP_BODY:
4653 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004654 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004655 memprintf(errmsg, "out of memory");
4656 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004657 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004658 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004659 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004660 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004661
4662 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004663 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4664 goto error;
4665 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004666 chk->expect.data.len = len;
4667 break;
4668 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004669 case TCPCHK_EXPECT_STRING_REGEX:
4670 case TCPCHK_EXPECT_BINARY_REGEX:
4671 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4672 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004673 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004674 if (!chk->expect.regex)
4675 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004676 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004677
4678 case TCPCHK_EXPECT_STRING_LF:
4679 case TCPCHK_EXPECT_BINARY_LF:
4680 case TCPCHK_EXPECT_HTTP_BODY_LF:
4681 LIST_INIT(&chk->expect.fmt);
4682 px->conf.args.ctx = ARGC_SRV;
4683 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4684 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4685 goto error;
4686 }
4687 break;
4688
Christopher Faulet39708192020-05-05 10:47:36 +02004689 case TCPCHK_EXPECT_HTTP_HEADER:
4690 if (!npat) {
4691 memprintf(errmsg, "unexpected error, undefined header name pattern");
4692 goto error;
4693 }
4694 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4695 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4696 if (!chk->expect.hdr.name_re)
4697 goto error;
4698 }
4699 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4700 px->conf.args.ctx = ARGC_SRV;
4701 LIST_INIT(&chk->expect.hdr.name_fmt);
4702 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4703 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4704 goto error;
4705 }
4706 }
4707 else {
4708 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4709 if (!isttest(chk->expect.hdr.name)) {
4710 memprintf(errmsg, "out of memory");
4711 goto error;
4712 }
4713 }
4714
4715 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4716 chk->expect.hdr.value = IST_NULL;
4717 break;
4718 }
4719
4720 if (!vpat) {
4721 memprintf(errmsg, "unexpected error, undefined header value pattern");
4722 goto error;
4723 }
4724 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4725 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4726 if (!chk->expect.hdr.value_re)
4727 goto error;
4728 }
4729 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4730 px->conf.args.ctx = ARGC_SRV;
4731 LIST_INIT(&chk->expect.hdr.value_fmt);
4732 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4733 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4734 goto error;
4735 }
4736 }
4737 else {
4738 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4739 if (!isttest(chk->expect.hdr.value)) {
4740 memprintf(errmsg, "out of memory");
4741 goto error;
4742 }
4743 }
4744
Christopher Faulet61cc8522020-04-20 14:54:42 +02004745 break;
4746 case TCPCHK_EXPECT_CUSTOM:
4747 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4748 break;
4749 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004750 memprintf(errmsg, "pattern not found");
4751 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004752 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004753
Christopher Faulet61cc8522020-04-20 14:54:42 +02004754 /* All tcp-check expect points back to the first inverse expect rule in
4755 * a chain of one or more expect rule, potentially itself.
4756 */
4757 chk->expect.head = chk;
4758 list_for_each_entry_rev(prev_check, rules, list) {
4759 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4760 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4761 chk->expect.head = prev_check;
4762 continue;
4763 }
4764 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4765 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004766 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004767 return chk;
4768
4769 error:
4770 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004771 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004772 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004773 return NULL;
4774}
4775
Christopher Faulet61cc8522020-04-20 14:54:42 +02004776/* Overwrites fields of the old http send rule with those of the new one. When
4777 * replaced, old values are freed and replaced by the new ones. New values are
4778 * not copied but transferred. At the end <new> should be empty and can be
4779 * safely released. This function never fails.
4780 */
4781static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004782{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004783 struct logformat_node *lf, *lfb;
4784 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004785
Christopher Faulet404f9192020-04-09 23:13:54 +02004786
Christopher Faulet61cc8522020-04-20 14:54:42 +02004787 if (new->send.http.meth.str.area) {
4788 free(old->send.http.meth.str.area);
4789 old->send.http.meth.meth = new->send.http.meth.meth;
4790 old->send.http.meth.str.area = new->send.http.meth.str.area;
4791 old->send.http.meth.str.data = new->send.http.meth.str.data;
4792 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004793 }
4794
Christopher Faulet61cc8522020-04-20 14:54:42 +02004795 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4796 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004797 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004798 else
4799 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4800 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4801 old->send.http.uri = new->send.http.uri;
4802 new->send.http.uri = IST_NULL;
4803 }
4804 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4805 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004806 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004807 else
4808 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4809 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4810 LIST_INIT(&old->send.http.uri_fmt);
4811 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4812 LIST_DEL(&lf->list);
4813 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4814 }
4815 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004816
Christopher Faulet61cc8522020-04-20 14:54:42 +02004817 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004818 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004819 old->send.http.vsn = new->send.http.vsn;
4820 new->send.http.vsn = IST_NULL;
4821 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004822
Christopher Faulet61cc8522020-04-20 14:54:42 +02004823 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4824 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4825 LIST_DEL(&hdr->list);
4826 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004827 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004828
4829 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4830 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004831 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004832 else
4833 free_tcpcheck_fmt(&old->send.http.body_fmt);
4834 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4835 old->send.http.body = new->send.http.body;
4836 new->send.http.body = IST_NULL;
4837 }
4838 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4839 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004840 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004841 else
4842 free_tcpcheck_fmt(&old->send.http.body_fmt);
4843 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4844 LIST_INIT(&old->send.http.body_fmt);
4845 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4846 LIST_DEL(&lf->list);
4847 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4848 }
4849 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004850}
4851
Christopher Faulet61cc8522020-04-20 14:54:42 +02004852/* Internal function used to add an http-check rule in a list during the config
4853 * parsing step. Depending on its type, and the previously inserted rules, a
4854 * specific action may be performed or an error may be reported. This functions
4855 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4856 * message.
4857 */
4858static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004859{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004860 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004861
Christopher Faulet61cc8522020-04-20 14:54:42 +02004862 /* the implicit send rule coming from an "option httpchk" line must be
4863 * merged with the first explici http-check send rule, if
4864 * any. Depdending the declaration order some tests are required.
4865 *
4866 * Some tests is also required for other kinds of http-check rules to be
4867 * sure the ruleset remains valid.
4868 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004869
Christopher Faulet61cc8522020-04-20 14:54:42 +02004870 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004871 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004872 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4873 * following tests are performed :
4874 *
4875 * 1- If there is no such rule or if it is not a send rule, the implicit send
4876 * rule is pushed in front of the ruleset
4877 *
4878 * 2- If it is another implicit send rule, it is replaced with the new one.
4879 *
4880 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004881 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004882 * new send rule (the implicit one).
4883 */
4884 r = get_first_tcpcheck_rule(rules);
4885 if (r && r->action == TCPCHK_ACT_CONNECT)
4886 r = get_next_tcpcheck_rule(rules, r);
4887 if (!r || r->action != TCPCHK_ACT_SEND)
4888 LIST_ADD(rules->list, &chk->list);
4889 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4890 LIST_DEL(&r->list);
4891 free_tcpcheck(r, 0);
4892 LIST_ADD(rules->list, &chk->list);
4893 }
4894 else {
4895 tcpcheck_overwrite_send_http_rule(r, chk);
4896 free_tcpcheck(chk, 0);
4897 }
4898 }
4899 else {
4900 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4901 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4902 * with an existing implicit send rule, if any. At the end, if there is no error,
4903 * the rule is appended to the list.
4904 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004905
Christopher Faulet61cc8522020-04-20 14:54:42 +02004906 r = get_last_tcpcheck_rule(rules);
4907 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4908 /* no error */;
4909 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4910 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4911 chk->index+1);
4912 return 0;
4913 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004914 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004915 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4916 chk->index+1);
4917 return 0;
4918 }
4919 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4920 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4921 chk->index+1);
4922 return 0;
4923 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004924
Christopher Faulet61cc8522020-04-20 14:54:42 +02004925 if (chk->action == TCPCHK_ACT_SEND) {
4926 r = get_first_tcpcheck_rule(rules);
4927 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4928 tcpcheck_overwrite_send_http_rule(r, chk);
4929 free_tcpcheck(chk, 0);
4930 LIST_DEL(&r->list);
4931 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4932 chk = r;
4933 }
4934 }
4935 LIST_ADDQ(rules->list, &chk->list);
4936 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004937 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004938}
4939
Christopher Faulet61cc8522020-04-20 14:54:42 +02004940/**************************************************************************/
4941/************************** Init/deinit checks ****************************/
4942/**************************************************************************/
4943static const char *init_check(struct check *check, int type)
4944{
4945 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004946
Christopher Faulet61cc8522020-04-20 14:54:42 +02004947 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4948 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004949
Christopher Faulet61cc8522020-04-20 14:54:42 +02004950 check->bi.area = calloc(check->bi.size, sizeof(char));
4951 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004952
Christopher Faulet61cc8522020-04-20 14:54:42 +02004953 if (!check->bi.area || !check->bo.area)
4954 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004955
Christopher Faulet61cc8522020-04-20 14:54:42 +02004956 check->wait_list.tasklet = tasklet_new();
4957 if (!check->wait_list.tasklet)
4958 return "out of memory while allocating check tasklet";
4959 check->wait_list.events = 0;
4960 check->wait_list.tasklet->process = event_srv_chk_io;
4961 check->wait_list.tasklet->context = check;
4962 return NULL;
4963}
4964
4965void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004966{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004967 task_destroy(check->task);
4968 if (check->wait_list.tasklet)
4969 tasklet_free(check->wait_list.tasklet);
4970
4971 free(check->bi.area);
4972 free(check->bo.area);
4973 if (check->cs) {
4974 free(check->cs->conn);
4975 check->cs->conn = NULL;
4976 cs_free(check->cs);
4977 check->cs = NULL;
4978 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004979}
4980
Christopher Faulet61cc8522020-04-20 14:54:42 +02004981/* manages a server health-check. Returns the time the task accepts to wait, or
4982 * TIME_ETERNITY for infinity.
4983 */
4984static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004985{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004986 struct check *check = context;
4987
4988 if (check->type == PR_O2_EXT_CHK)
4989 return process_chk_proc(t, context, state);
4990 return process_chk_conn(t, context, state);
4991
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004992}
4993
Christopher Faulet61cc8522020-04-20 14:54:42 +02004994
4995static int start_check_task(struct check *check, int mininter,
4996 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004997{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004998 struct task *t;
4999 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005000
Christopher Faulet61cc8522020-04-20 14:54:42 +02005001 if (check->type == PR_O2_EXT_CHK)
5002 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005003
Christopher Faulet61cc8522020-04-20 14:54:42 +02005004 /* task for the check */
5005 if ((t = task_new(thread_mask)) == NULL) {
5006 ha_alert("Starting [%s:%s] check: out of memory.\n",
5007 check->server->proxy->id, check->server->id);
5008 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005009 }
5010
Christopher Faulet61cc8522020-04-20 14:54:42 +02005011 check->task = t;
5012 t->process = process_chk;
5013 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005014
Christopher Faulet61cc8522020-04-20 14:54:42 +02005015 if (mininter < srv_getinter(check))
5016 mininter = srv_getinter(check);
5017
5018 if (global.max_spread_checks && mininter > global.max_spread_checks)
5019 mininter = global.max_spread_checks;
5020
5021 /* check this every ms */
5022 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5023 check->start = now;
5024 task_queue(t);
5025
5026 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005027}
5028
Christopher Faulet61cc8522020-04-20 14:54:42 +02005029/* updates the server's weight during a warmup stage. Once the final weight is
5030 * reached, the task automatically stops. Note that any server status change
5031 * must have updated s->last_change accordingly.
5032 */
5033static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005034{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005035 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005036
Christopher Faulet61cc8522020-04-20 14:54:42 +02005037 /* by default, plan on stopping the task */
5038 t->expire = TICK_ETERNITY;
5039 if ((s->next_admin & SRV_ADMF_MAINT) ||
5040 (s->next_state != SRV_ST_STARTING))
5041 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005042
Christopher Faulet61cc8522020-04-20 14:54:42 +02005043 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005044
Christopher Faulet61cc8522020-04-20 14:54:42 +02005045 /* recalculate the weights and update the state */
5046 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005047
Christopher Faulet61cc8522020-04-20 14:54:42 +02005048 /* probably that we can refill this server with a bit more connections */
5049 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005050
Christopher Faulet61cc8522020-04-20 14:54:42 +02005051 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005052
Christopher Faulet61cc8522020-04-20 14:54:42 +02005053 /* get back there in 1 second or 1/20th of the slowstart interval,
5054 * whichever is greater, resulting in small 5% steps.
5055 */
5056 if (s->next_state == SRV_ST_STARTING)
5057 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5058 return t;
5059}
5060
5061/*
5062 * Start health-check.
5063 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5064 */
5065static int start_checks()
5066{
5067
5068 struct proxy *px;
5069 struct server *s;
5070 struct task *t;
5071 int nbcheck=0, mininter=0, srvpos=0;
5072
5073 /* 0- init the dummy frontend used to create all checks sessions */
5074 init_new_proxy(&checks_fe);
5075 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5076 checks_fe.mode = PR_MODE_TCP;
5077 checks_fe.maxconn = 0;
5078 checks_fe.conn_retries = CONN_RETRIES;
5079 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5080 checks_fe.timeout.client = TICK_ETERNITY;
5081
5082 /* 1- count the checkers to run simultaneously.
5083 * We also determine the minimum interval among all of those which
5084 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5085 * will be used to spread their start-up date. Those which have
5086 * a shorter interval will start independently and will not dictate
5087 * too short an interval for all others.
5088 */
5089 for (px = proxies_list; px; px = px->next) {
5090 for (s = px->srv; s; s = s->next) {
5091 if (s->slowstart) {
5092 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5093 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5094 return ERR_ALERT | ERR_FATAL;
5095 }
5096 /* We need a warmup task that will be called when the server
5097 * state switches from down to up.
5098 */
5099 s->warmup = t;
5100 t->process = server_warmup;
5101 t->context = s;
5102 /* server can be in this state only because of */
5103 if (s->next_state == SRV_ST_STARTING)
5104 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 +02005105 }
5106
Christopher Faulet61cc8522020-04-20 14:54:42 +02005107 if (s->check.state & CHK_ST_CONFIGURED) {
5108 nbcheck++;
5109 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5110 (!mininter || mininter > srv_getinter(&s->check)))
5111 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005112 }
5113
Christopher Faulet61cc8522020-04-20 14:54:42 +02005114 if (s->agent.state & CHK_ST_CONFIGURED) {
5115 nbcheck++;
5116 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5117 (!mininter || mininter > srv_getinter(&s->agent)))
5118 mininter = srv_getinter(&s->agent);
5119 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005120 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005121 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005122
Christopher Faulet61cc8522020-04-20 14:54:42 +02005123 if (!nbcheck)
5124 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005125
Christopher Faulet61cc8522020-04-20 14:54:42 +02005126 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005127
Christopher Faulet61cc8522020-04-20 14:54:42 +02005128 /*
5129 * 2- start them as far as possible from each others. For this, we will
5130 * start them after their interval set to the min interval divided by
5131 * the number of servers, weighted by the server's position in the list.
5132 */
5133 for (px = proxies_list; px; px = px->next) {
5134 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5135 if (init_pid_list()) {
5136 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5137 return ERR_ALERT | ERR_FATAL;
5138 }
5139 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005140
Christopher Faulet61cc8522020-04-20 14:54:42 +02005141 for (s = px->srv; s; s = s->next) {
5142 /* A task for the main check */
5143 if (s->check.state & CHK_ST_CONFIGURED) {
5144 if (s->check.type == PR_O2_EXT_CHK) {
5145 if (!prepare_external_check(&s->check))
5146 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005147 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005148 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5149 return ERR_ALERT | ERR_FATAL;
5150 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005151 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005152
Christopher Faulet61cc8522020-04-20 14:54:42 +02005153 /* A task for a auxiliary agent check */
5154 if (s->agent.state & CHK_ST_CONFIGURED) {
5155 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5156 return ERR_ALERT | ERR_FATAL;
5157 }
5158 srvpos++;
5159 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005160 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005161 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005162 return 0;
5163}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005164
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005165
Christopher Faulet61cc8522020-04-20 14:54:42 +02005166/*
5167 * Return value:
5168 * the port to be used for the health check
5169 * 0 in case no port could be found for the check
5170 */
5171static int srv_check_healthcheck_port(struct check *chk)
5172{
5173 int i = 0;
5174 struct server *srv = NULL;
5175
5176 srv = chk->server;
5177
5178 /* by default, we use the health check port ocnfigured */
5179 if (chk->port > 0)
5180 return chk->port;
5181
5182 /* try to get the port from check_core.addr if check.port not set */
5183 i = get_host_port(&chk->addr);
5184 if (i > 0)
5185 return i;
5186
5187 /* try to get the port from server address */
5188 /* prevent MAPPORTS from working at this point, since checks could
5189 * not be performed in such case (MAPPORTS impose a relative ports
5190 * based on live traffic)
5191 */
5192 if (srv->flags & SRV_F_MAPPORTS)
5193 return 0;
5194
5195 i = srv->svc_port; /* by default */
5196 if (i > 0)
5197 return i;
5198
5199 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005200}
5201
Christopher Faulet61cc8522020-04-20 14:54:42 +02005202/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5203 * if an error occurred.
5204 */
5205static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005206{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005207 const char *err;
5208 struct tcpcheck_rule *r;
5209 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005210
Christopher Faulet61cc8522020-04-20 14:54:42 +02005211 if (!srv->do_check)
5212 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005213
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005214
Christopher Faulet61cc8522020-04-20 14:54:42 +02005215 /* If neither a port nor an addr was specified and no check transport
5216 * layer is forced, then the transport layer used by the checks is the
5217 * same as for the production traffic. Otherwise we use raw_sock by
5218 * default, unless one is specified.
5219 */
5220 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5221 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5222 srv->check.use_ssl = srv->use_ssl;
5223 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005224 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005225 else if (srv->check.use_ssl == 1)
5226 srv->check.xprt = xprt_get(XPRT_SSL);
5227 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005228 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02005229 else if (srv->check.use_ssl == 1)
5230 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005231
Christopher Faulet12882cf2020-04-23 15:50:18 +02005232 /* Inherit the mux protocol from the server if not already defined for
5233 * the check
5234 */
5235 if (srv->mux_proto && !srv->check.mux_proto)
5236 srv->check.mux_proto = srv->mux_proto;
5237
Christopher Faulet61cc8522020-04-20 14:54:42 +02005238 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005239
Christopher Faulet61cc8522020-04-20 14:54:42 +02005240 /* We need at least a service port, a check port or the first tcp-check
5241 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5242 */
5243 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5244 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5245 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005246
Christopher Faulet61cc8522020-04-20 14:54:42 +02005247 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5248 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5249 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5250 ret |= ERR_ALERT | ERR_ABORT;
5251 goto out;
5252 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005253
Christopher Faulet61cc8522020-04-20 14:54:42 +02005254 /* search the first action (connect / send / expect) in the list */
5255 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5256 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5257 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5258 "nor tcp_check rule 'connect' with port information.\n",
5259 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5260 ret |= ERR_ALERT | ERR_ABORT;
5261 goto out;
5262 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005263
Christopher Faulet61cc8522020-04-20 14:54:42 +02005264 /* scan the tcp-check ruleset to ensure a port has been configured */
5265 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5266 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5267 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5268 "and a tcp_check rule 'connect' with no port information.\n",
5269 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5270 ret |= ERR_ALERT | ERR_ABORT;
5271 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005272 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005273 }
5274
Christopher Faulet61cc8522020-04-20 14:54:42 +02005275 init:
5276 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5277 struct tcpcheck_ruleset *rs = NULL;
5278 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5279 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005280
Christopher Faulet61cc8522020-04-20 14:54:42 +02005281 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5282 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005283
Christopher Faulet61cc8522020-04-20 14:54:42 +02005284 rs = find_tcpcheck_ruleset("*tcp-check");
5285 if (!rs) {
5286 rs = create_tcpcheck_ruleset("*tcp-check");
5287 if (rs == NULL) {
5288 ha_alert("config: %s '%s': out of memory.\n",
5289 proxy_type_str(srv->proxy), srv->proxy->id);
5290 ret |= ERR_ALERT | ERR_FATAL;
5291 goto out;
5292 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005293 }
5294
Christopher Faulet61cc8522020-04-20 14:54:42 +02005295 free_tcpcheck_vars(&rules->preset_vars);
5296 rules->list = &rs->rules;
5297 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005298 }
5299
Christopher Faulet61cc8522020-04-20 14:54:42 +02005300 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5301 if (err) {
5302 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5303 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5304 ret |= ERR_ALERT | ERR_ABORT;
5305 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005306 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005307 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5308 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005309
Christopher Faulet61cc8522020-04-20 14:54:42 +02005310 out:
5311 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005312}
5313
Christopher Faulet61cc8522020-04-20 14:54:42 +02005314/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5315 * if an error occurred.
5316 */
5317static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005318{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005319 struct tcpcheck_rule *chk;
5320 const char *err;
5321 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005322
Christopher Faulet61cc8522020-04-20 14:54:42 +02005323 if (!srv->do_agent)
5324 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005325
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005326 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005327 * implicit one is inserted before all others.
5328 */
5329 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5330 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5331 chk = calloc(1, sizeof(*chk));
5332 if (!chk) {
5333 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5334 " to agent-check for server '%s' (out of memory).\n",
5335 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5336 ret |= ERR_ALERT | ERR_FATAL;
5337 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005338 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005339 chk->action = TCPCHK_ACT_CONNECT;
5340 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5341 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005342 }
5343
Christopher Faulete5870d82020-04-15 11:32:03 +02005344
Christopher Faulet61cc8522020-04-20 14:54:42 +02005345 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5346 if (err) {
5347 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5348 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5349 ret |= ERR_ALERT | ERR_ABORT;
5350 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005351 }
5352
Christopher Faulet61cc8522020-04-20 14:54:42 +02005353 if (!srv->agent.inter)
5354 srv->agent.inter = srv->check.inter;
5355
5356 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5357 global.maxsock++;
5358
5359 out:
5360 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005361}
5362
Christopher Faulet61cc8522020-04-20 14:54:42 +02005363/* Check tcp-check health-check configuration for the proxy <px>. */
5364static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005365{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005366 struct tcpcheck_rule *chk, *back;
5367 char *comment = NULL, *errmsg = NULL;
5368 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5369 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005370
Christopher Faulet61cc8522020-04-20 14:54:42 +02005371 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5372 deinit_proxy_tcpcheck(px);
5373 goto out;
5374 }
5375
5376 free(px->check_command);
5377 free(px->check_path);
5378 px->check_command = px->check_path = NULL;
5379
5380 if (!px->tcpcheck_rules.list) {
5381 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5382 ret |= ERR_ALERT | ERR_FATAL;
5383 goto out;
5384 }
5385
5386 /* HTTP ruleset only : */
5387 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5388 struct tcpcheck_rule *next;
5389
5390 /* move remaining implicit send rule from "option httpchk" line to the right place.
5391 * If such rule exists, it must be the first one. In this case, the rule is moved
5392 * after the first connect rule, if any. Otherwise, nothing is done.
5393 */
5394 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5395 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5396 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5397 if (next && next->action == TCPCHK_ACT_CONNECT) {
5398 LIST_DEL(&chk->list);
5399 LIST_ADD(&next->list, &chk->list);
5400 chk->index = next->index;
5401 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005402 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005403
5404 /* add implicit expect rule if the last one is a send. It is inherited from previous
5405 * versions where the http expect rule was optional. Now it is possible to chained
5406 * send/expect rules but the last expect may still be implicit.
5407 */
5408 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5409 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005410 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005411 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5412 px->conf.file, px->conf.line, &errmsg);
5413 if (!next) {
5414 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5415 "(%s).\n", px->id, errmsg);
5416 free(errmsg);
5417 ret |= ERR_ALERT | ERR_FATAL;
5418 goto out;
5419 }
5420 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5421 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005422 }
5423 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005424
5425 /* For all ruleset: */
5426
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005427 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005428 * implicit one is inserted before all others.
5429 */
5430 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5431 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5432 chk = calloc(1, sizeof(*chk));
5433 if (!chk) {
5434 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5435 "(out of memory).\n", px->id);
5436 ret |= ERR_ALERT | ERR_FATAL;
5437 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005438 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005439 chk->action = TCPCHK_ACT_CONNECT;
5440 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5441 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5442 }
5443
5444 /* Remove all comment rules. To do so, when a such rule is found, the
5445 * comment is assigned to the following rule(s).
5446 */
5447 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5448 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5449 free(comment);
5450 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005451 }
5452
Christopher Faulet61cc8522020-04-20 14:54:42 +02005453 prev_action = chk->action;
5454 switch (chk->action) {
5455 case TCPCHK_ACT_COMMENT:
5456 free(comment);
5457 comment = chk->comment;
5458 LIST_DEL(&chk->list);
5459 free(chk);
5460 break;
5461 case TCPCHK_ACT_CONNECT:
5462 if (!chk->comment && comment)
5463 chk->comment = strdup(comment);
5464 /* fall though */
5465 case TCPCHK_ACT_ACTION_KW:
5466 free(comment);
5467 comment = NULL;
5468 break;
5469 case TCPCHK_ACT_SEND:
5470 case TCPCHK_ACT_EXPECT:
5471 if (!chk->comment && comment)
5472 chk->comment = strdup(comment);
5473 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005474 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005475 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005476 free(comment);
5477 comment = NULL;
5478
5479 out:
5480 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005481}
5482
Christopher Faulet61cc8522020-04-20 14:54:42 +02005483void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005484{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005485 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5486 px->tcpcheck_rules.flags = 0;
5487 px->tcpcheck_rules.list = NULL;
5488}
Christopher Faulete5870d82020-04-15 11:32:03 +02005489
Christopher Faulet61cc8522020-04-20 14:54:42 +02005490static void deinit_srv_check(struct server *srv)
5491{
5492 if (srv->check.state & CHK_ST_CONFIGURED)
5493 free_check(&srv->check);
5494 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5495 srv->do_check = 0;
5496}
Christopher Faulete5870d82020-04-15 11:32:03 +02005497
Christopher Faulet61cc8522020-04-20 14:54:42 +02005498
5499static void deinit_srv_agent_check(struct server *srv)
5500{
5501 if (srv->agent.tcpcheck_rules) {
5502 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5503 free(srv->agent.tcpcheck_rules);
5504 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005505 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005506
Christopher Faulet61cc8522020-04-20 14:54:42 +02005507 if (srv->agent.state & CHK_ST_CONFIGURED)
5508 free_check(&srv->agent);
5509
5510 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5511 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005512}
5513
Christopher Faulet61cc8522020-04-20 14:54:42 +02005514static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005515{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005516 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005517 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005518 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005519
Christopher Fauletd7cee712020-04-21 13:45:00 +02005520 node = ebpt_first(&shared_tcpchecks);
5521 while (node) {
5522 next = ebpt_next(node);
5523 ebpt_delete(node);
5524 free(node->key);
5525 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005526 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5527 LIST_DEL(&r->list);
5528 free_tcpcheck(r, 0);
5529 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005530 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005531 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005532 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005533}
Christopher Faulete5870d82020-04-15 11:32:03 +02005534
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005535
Christopher Faulet61cc8522020-04-20 14:54:42 +02005536REGISTER_POST_SERVER_CHECK(init_srv_check);
5537REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5538REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5539REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005540
Christopher Faulet61cc8522020-04-20 14:54:42 +02005541REGISTER_SERVER_DEINIT(deinit_srv_check);
5542REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5543REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5544REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005545
Christopher Faulet61cc8522020-04-20 14:54:42 +02005546/**************************************************************************/
5547/****************************** Email alerts ******************************/
5548/* NOTE: It may be pertinent to use an applet to handle email alerts */
5549/* instead of a tcp-check ruleset */
5550/**************************************************************************/
5551void email_alert_free(struct email_alert *alert)
5552{
5553 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005554
Christopher Faulet61cc8522020-04-20 14:54:42 +02005555 if (!alert)
5556 return;
5557
5558 if (alert->rules.list) {
5559 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5560 LIST_DEL(&rule->list);
5561 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005562 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005563 free_tcpcheck_vars(&alert->rules.preset_vars);
5564 free(alert->rules.list);
5565 alert->rules.list = NULL;
5566 }
5567 pool_free(pool_head_email_alert, alert);
5568}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005569
Christopher Faulet61cc8522020-04-20 14:54:42 +02005570static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5571{
5572 struct check *check = context;
5573 struct email_alertq *q;
5574 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005575
Christopher Faulet61cc8522020-04-20 14:54:42 +02005576 q = container_of(check, typeof(*q), check);
5577
5578 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5579 while (1) {
5580 if (!(check->state & CHK_ST_ENABLED)) {
5581 if (LIST_ISEMPTY(&q->email_alerts)) {
5582 /* All alerts processed, queue the task */
5583 t->expire = TICK_ETERNITY;
5584 task_queue(t);
5585 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005586 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005587
5588 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5589 LIST_DEL(&alert->list);
5590 t->expire = now_ms;
5591 check->tcpcheck_rules = &alert->rules;
5592 check->status = HCHK_STATUS_INI;
5593 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005594 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005595
5596 process_chk(t, context, state);
5597 if (check->state & CHK_ST_INPROGRESS)
5598 break;
5599
5600 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5601 email_alert_free(alert);
5602 check->tcpcheck_rules = NULL;
5603 check->server = NULL;
5604 check->state &= ~CHK_ST_ENABLED;
5605 }
5606 end:
5607 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5608 return t;
5609}
5610
5611/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5612 *
5613 * The function returns 1 in success case, otherwise, it returns 0 and err is
5614 * filled.
5615 */
5616int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5617{
5618 struct mailer *mailer;
5619 struct email_alertq *queues;
5620 const char *err_str;
5621 int i = 0;
5622
5623 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5624 memprintf(err, "out of memory while allocating mailer alerts queues");
5625 goto fail_no_queue;
5626 }
5627
5628 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5629 struct email_alertq *q = &queues[i];
5630 struct check *check = &q->check;
5631 struct task *t;
5632
5633 LIST_INIT(&q->email_alerts);
5634 HA_SPIN_INIT(&q->lock);
5635 check->inter = mls->timeout.mail;
5636 check->rise = DEF_AGENT_RISETIME;
5637 check->proxy = p;
5638 check->fall = DEF_AGENT_FALLTIME;
5639 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5640 memprintf(err, "%s", err_str);
5641 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005642 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005643
5644 check->xprt = mailer->xprt;
5645 check->addr = mailer->addr;
5646 check->port = get_host_port(&mailer->addr);
5647
5648 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5649 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005650 goto error;
5651 }
5652
Christopher Faulet61cc8522020-04-20 14:54:42 +02005653 check->task = t;
5654 t->process = process_email_alert;
5655 t->context = check;
5656
5657 /* check this in one ms */
5658 t->expire = TICK_ETERNITY;
5659 check->start = now;
5660 task_queue(t);
5661 }
5662
5663 mls->users++;
5664 free(p->email_alert.mailers.name);
5665 p->email_alert.mailers.m = mls;
5666 p->email_alert.queues = queues;
5667 return 0;
5668
5669 error:
5670 for (i = 0; i < mls->count; i++) {
5671 struct email_alertq *q = &queues[i];
5672 struct check *check = &q->check;
5673
5674 free_check(check);
5675 }
5676 free(queues);
5677 fail_no_queue:
5678 return 1;
5679}
5680
5681static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5682{
5683 struct tcpcheck_rule *tcpcheck, *prev_check;
5684 struct tcpcheck_expect *expect;
5685
5686 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5687 return 0;
5688 memset(tcpcheck, 0, sizeof(*tcpcheck));
5689 tcpcheck->action = TCPCHK_ACT_EXPECT;
5690
5691 expect = &tcpcheck->expect;
5692 expect->type = TCPCHK_EXPECT_STRING;
5693 LIST_INIT(&expect->onerror_fmt);
5694 LIST_INIT(&expect->onsuccess_fmt);
5695 expect->ok_status = HCHK_STATUS_L7OKD;
5696 expect->err_status = HCHK_STATUS_L7RSP;
5697 expect->tout_status = HCHK_STATUS_L7TOUT;
5698 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005699 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005700 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5701 return 0;
5702 }
5703
5704 /* All tcp-check expect points back to the first inverse expect rule
5705 * in a chain of one or more expect rule, potentially itself.
5706 */
5707 tcpcheck->expect.head = tcpcheck;
5708 list_for_each_entry_rev(prev_check, rules->list, list) {
5709 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5710 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5711 tcpcheck->expect.head = prev_check;
5712 continue;
5713 }
5714 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5715 break;
5716 }
5717 LIST_ADDQ(rules->list, &tcpcheck->list);
5718 return 1;
5719}
5720
5721static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5722{
5723 struct tcpcheck_rule *tcpcheck;
5724 struct tcpcheck_send *send;
5725 const char *in;
5726 char *dst;
5727 int i;
5728
5729 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5730 return 0;
5731 memset(tcpcheck, 0, sizeof(*tcpcheck));
5732 tcpcheck->action = TCPCHK_ACT_SEND;
5733
5734 send = &tcpcheck->send;
5735 send->type = TCPCHK_SEND_STRING;
5736
5737 for (i = 0; strs[i]; i++)
5738 send->data.len += strlen(strs[i]);
5739
Christopher Fauletb61caf42020-04-21 10:57:42 +02005740 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005741 if (!isttest(send->data)) {
5742 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5743 return 0;
5744 }
5745
Christopher Fauletb61caf42020-04-21 10:57:42 +02005746 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005747 for (i = 0; strs[i]; i++)
5748 for (in = strs[i]; (*dst = *in++); dst++);
5749 *dst = 0;
5750
5751 LIST_ADDQ(rules->list, &tcpcheck->list);
5752 return 1;
5753}
5754
5755static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5756 struct email_alertq *q, const char *msg)
5757{
5758 struct email_alert *alert;
5759 struct tcpcheck_rule *tcpcheck;
5760 struct check *check = &q->check;
5761
5762 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5763 goto error;
5764 LIST_INIT(&alert->list);
5765 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5766 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5767 if (!alert->rules.list)
5768 goto error;
5769 LIST_INIT(alert->rules.list);
5770 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5771 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005772
Christopher Faulet61cc8522020-04-20 14:54:42 +02005773 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5774 goto error;
5775 memset(tcpcheck, 0, sizeof(*tcpcheck));
5776 tcpcheck->action = TCPCHK_ACT_CONNECT;
5777 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005778
Christopher Faulet61cc8522020-04-20 14:54:42 +02005779 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005780
Christopher Faulet61cc8522020-04-20 14:54:42 +02005781 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005782 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005783
5784 {
5785 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5786 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5787 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005788 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005789
Christopher Faulet61cc8522020-04-20 14:54:42 +02005790 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5791 goto error;
5792
5793 {
5794 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5795 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005796 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005797 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005798
5799 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5800 goto error;
5801
5802 {
5803 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5804 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005805 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005806 }
5807
Christopher Faulet61cc8522020-04-20 14:54:42 +02005808 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5809 goto error;
5810
5811 {
5812 const char * const strs[2] = { "DATA\r\n" };
5813 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005814 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005815 }
5816
5817 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5818 goto error;
5819
5820 {
5821 struct tm tm;
5822 char datestr[48];
5823 const char * const strs[18] = {
5824 "From: ", p->email_alert.from, "\r\n",
5825 "To: ", p->email_alert.to, "\r\n",
5826 "Date: ", datestr, "\r\n",
5827 "Subject: [HAproxy Alert] ", msg, "\r\n",
5828 "\r\n",
5829 msg, "\r\n",
5830 "\r\n",
5831 ".\r\n",
5832 NULL
5833 };
5834
5835 get_localtime(date.tv_sec, &tm);
5836
5837 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005838 goto error;
5839 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005840
5841 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005842 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005843 }
5844
5845 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005846 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005847
5848 {
5849 const char * const strs[2] = { "QUIT\r\n" };
5850 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5851 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005852 }
5853
Christopher Faulet61cc8522020-04-20 14:54:42 +02005854 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5855 goto error;
5856
5857 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5858 task_wakeup(check->task, TASK_WOKEN_MSG);
5859 LIST_ADDQ(&q->email_alerts, &alert->list);
5860 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5861 return 1;
5862
5863error:
5864 email_alert_free(alert);
5865 return 0;
5866}
5867
5868static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5869{
5870 int i;
5871 struct mailer *mailer;
5872
5873 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5874 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5875 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5876 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5877 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005878 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005879 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005880
Christopher Faulet61cc8522020-04-20 14:54:42 +02005881 return;
5882}
5883
5884/*
5885 * Send email alert if configured.
5886 */
5887void send_email_alert(struct server *s, int level, const char *format, ...)
5888{
5889 va_list argp;
5890 char buf[1024];
5891 int len;
5892 struct proxy *p = s->proxy;
5893
5894 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5895 return;
5896
5897 va_start(argp, format);
5898 len = vsnprintf(buf, sizeof(buf), format, argp);
5899 va_end(argp);
5900
5901 if (len < 0 || len >= sizeof(buf)) {
5902 ha_alert("Email alert [%s] could not format message\n", p->id);
5903 return;
5904 }
5905
5906 enqueue_email_alert(p, s, buf);
5907}
5908
5909/**************************************************************************/
5910/************************** Check sample fetches **************************/
5911/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005912
Christopher Faulet61cc8522020-04-20 14:54:42 +02005913static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005914 { /* END */ },
5915}};
5916
5917INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5918
5919
5920/**************************************************************************/
5921/************************ Check's parsing functions ***********************/
5922/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005923/* Parses the "tcp-check" proxy keyword */
5924static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5925 struct proxy *defpx, const char *file, int line,
5926 char **errmsg)
5927{
Christopher Faulet404f9192020-04-09 23:13:54 +02005928 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005929 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005930 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005931
5932 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5933 ret = 1;
5934
Christopher Faulet404f9192020-04-09 23:13:54 +02005935 /* Deduce the ruleset name from the proxy info */
5936 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5937 ((curpx == defpx) ? "defaults" : curpx->id),
5938 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005939
Christopher Faulet61cc8522020-04-20 14:54:42 +02005940 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005941 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005942 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005943 if (rs == NULL) {
5944 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005945 goto error;
5946 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005947 }
5948
Gaetan Rivet5301b012020-02-25 17:19:17 +01005949 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005950 if (!LIST_ISEMPTY(&rs->rules)) {
5951 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005952 index = chk->index + 1;
5953 }
5954
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005955 cur_arg = 1;
5956 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005957 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005958 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5959 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005960 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005961 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005962 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005963 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005964 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005965 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005966 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5967
5968 if (!kw) {
5969 action_kw_tcp_check_build_list(&trash);
5970 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5971 "%s%s. but got '%s'",
5972 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5973 goto error;
5974 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005975 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005976 }
5977
5978 if (!chk) {
5979 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5980 goto error;
5981 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005982 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005983
5984 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005985 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005986 LIST_ADDQ(&rs->rules, &chk->list);
5987
5988 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005989 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005990 /* Use this ruleset if the proxy already has tcp-check enabled */
5991 curpx->tcpcheck_rules.list = &rs->rules;
5992 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5993 }
5994 else {
5995 /* mark this ruleset as unused for now */
5996 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
5997 }
5998
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005999 return ret;
6000
6001 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006002 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006003 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006004 return -1;
6005}
6006
Christopher Faulet51b129f2020-04-09 15:54:18 +02006007/* Parses the "http-check" proxy keyword */
6008static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6009 struct proxy *defpx, const char *file, int line,
6010 char **errmsg)
6011{
Christopher Faulete5870d82020-04-15 11:32:03 +02006012 struct tcpcheck_ruleset *rs = NULL;
6013 struct tcpcheck_rule *chk = NULL;
6014 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006015
6016 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6017 ret = 1;
6018
6019 cur_arg = 1;
6020 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6021 /* enable a graceful server shutdown on an HTTP 404 response */
6022 curpx->options |= PR_O_DISABLE404;
6023 if (too_many_args(1, args, errmsg, NULL))
6024 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006025 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006026 }
6027 else if (strcmp(args[cur_arg], "send-state") == 0) {
6028 /* enable emission of the apparent state of a server in HTTP checks */
6029 curpx->options2 |= PR_O2_CHK_SNDST;
6030 if (too_many_args(1, args, errmsg, NULL))
6031 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006032 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006033 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006034
Christopher Faulete5870d82020-04-15 11:32:03 +02006035 /* Deduce the ruleset name from the proxy info */
6036 chunk_printf(&trash, "*http-check-%s_%s-%d",
6037 ((curpx == defpx) ? "defaults" : curpx->id),
6038 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006039
Christopher Faulet61cc8522020-04-20 14:54:42 +02006040 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006041 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006042 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006043 if (rs == NULL) {
6044 memprintf(errmsg, "out of memory.\n");
6045 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006046 }
6047 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006048
Christopher Faulete5870d82020-04-15 11:32:03 +02006049 index = 0;
6050 if (!LIST_ISEMPTY(&rs->rules)) {
6051 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6052 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6053 index = chk->index + 1;
6054 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006055
Christopher Faulete5870d82020-04-15 11:32:03 +02006056 if (strcmp(args[cur_arg], "connect") == 0)
6057 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6058 else if (strcmp(args[cur_arg], "send") == 0)
6059 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6060 else if (strcmp(args[cur_arg], "expect") == 0)
6061 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6062 file, line, errmsg);
6063 else if (strcmp(args[cur_arg], "comment") == 0)
6064 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6065 else {
6066 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006067
Christopher Faulete5870d82020-04-15 11:32:03 +02006068 if (!kw) {
6069 action_kw_tcp_check_build_list(&trash);
6070 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6071 " 'send', 'expect'%s%s. but got '%s'",
6072 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6073 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006074 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006075 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6076 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006077
Christopher Faulete5870d82020-04-15 11:32:03 +02006078 if (!chk) {
6079 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6080 goto error;
6081 }
6082 ret = (*errmsg != NULL); /* Handle warning */
6083
6084 chk->index = index;
6085 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6086 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6087 /* Use this ruleset if the proxy already has http-check enabled */
6088 curpx->tcpcheck_rules.list = &rs->rules;
6089 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6090 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6091 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6092 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006093 goto error;
6094 }
6095 }
6096 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006097 /* mark this ruleset as unused for now */
6098 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6099 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006100 }
6101
Christopher Faulete5870d82020-04-15 11:32:03 +02006102 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006103 return ret;
6104
6105 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006106 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006107 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006108 return -1;
6109}
6110
Christopher Faulete9111b62020-04-09 18:12:08 +02006111/* Parses the "external-check" proxy keyword */
6112static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6113 struct proxy *defpx, const char *file, int line,
6114 char **errmsg)
6115{
6116 int cur_arg, ret = 0;
6117
6118 cur_arg = 1;
6119 if (!*(args[cur_arg])) {
6120 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6121 goto error;
6122 }
6123
6124 if (strcmp(args[cur_arg], "command") == 0) {
6125 if (too_many_args(2, args, errmsg, NULL))
6126 goto error;
6127 if (!*(args[cur_arg+1])) {
6128 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6129 goto error;
6130 }
6131 free(curpx->check_command);
6132 curpx->check_command = strdup(args[cur_arg+1]);
6133 }
6134 else if (strcmp(args[cur_arg], "path") == 0) {
6135 if (too_many_args(2, args, errmsg, NULL))
6136 goto error;
6137 if (!*(args[cur_arg+1])) {
6138 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6139 goto error;
6140 }
6141 free(curpx->check_path);
6142 curpx->check_path = strdup(args[cur_arg+1]);
6143 }
6144 else {
6145 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6146 args[0], args[1]);
6147 goto error;
6148 }
6149
6150 ret = (*errmsg != NULL); /* Handle warning */
6151 return ret;
6152
6153error:
6154 return -1;
6155}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006156
Christopher Faulet430e4802020-04-09 15:28:16 +02006157/* Parses the "option tcp-check" proxy keyword */
6158int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6159 const char *file, int line)
6160{
Christopher Faulet404f9192020-04-09 23:13:54 +02006161 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006162 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6163 int err_code = 0;
6164
6165 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6166 err_code |= ERR_WARN;
6167
6168 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6169 goto out;
6170
Christopher Faulet404f9192020-04-09 23:13:54 +02006171 curpx->options2 &= ~PR_O2_CHK_ANY;
6172 curpx->options2 |= PR_O2_TCPCHK_CHK;
6173
Christopher Fauletd7e63962020-04-17 20:15:59 +02006174 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006175 /* If a tcp-check rulesset is already set, do nothing */
6176 if (rules->list)
6177 goto out;
6178
6179 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6180 * get it.
6181 */
6182 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6183 goto curpx_ruleset;
6184
6185 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6186 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006187 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006188 if (rs)
6189 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006190 }
6191
Christopher Faulet404f9192020-04-09 23:13:54 +02006192 curpx_ruleset:
6193 /* Deduce the ruleset name from the proxy info */
6194 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6195 ((curpx == defpx) ? "defaults" : curpx->id),
6196 curpx->conf.file, curpx->conf.line);
6197
Christopher Faulet61cc8522020-04-20 14:54:42 +02006198 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006199 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006200 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006201 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006202 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6203 goto error;
6204 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006205 }
6206
Christopher Faulet404f9192020-04-09 23:13:54 +02006207 ruleset_found:
6208 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006209 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006210 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006211
6212 out:
6213 return err_code;
6214
6215 error:
6216 err_code |= ERR_ALERT | ERR_FATAL;
6217 goto out;
6218}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006219
6220/* Parses the "option redis-check" proxy keyword */
6221int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6222 const char *file, int line)
6223{
6224 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6225 static char *redis_res = "+PONG\r\n";
6226
6227 struct tcpcheck_ruleset *rs = NULL;
6228 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6229 struct tcpcheck_rule *chk;
6230 char *errmsg = NULL;
6231 int err_code = 0;
6232
6233 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6234 err_code |= ERR_WARN;
6235
6236 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6237 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006238
6239 curpx->options2 &= ~PR_O2_CHK_ANY;
6240 curpx->options2 |= PR_O2_TCPCHK_CHK;
6241
6242 free_tcpcheck_vars(&rules->preset_vars);
6243 rules->list = NULL;
6244 rules->flags = 0;
6245
Christopher Faulet61cc8522020-04-20 14:54:42 +02006246 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006247 if (rs)
6248 goto ruleset_found;
6249
Christopher Faulet61cc8522020-04-20 14:54:42 +02006250 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006251 if (rs == NULL) {
6252 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6253 goto error;
6254 }
6255
6256 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6257 1, curpx, &rs->rules, file, line, &errmsg);
6258 if (!chk) {
6259 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6260 goto error;
6261 }
6262 chk->index = 0;
6263 LIST_ADDQ(&rs->rules, &chk->list);
6264
6265 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6266 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006267 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006268 "on-success", "Redis server is ok",
6269 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006270 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006271 if (!chk) {
6272 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6273 goto error;
6274 }
6275 chk->index = 1;
6276 LIST_ADDQ(&rs->rules, &chk->list);
6277
Christopher Faulet33f05df2020-04-01 11:08:50 +02006278 ruleset_found:
6279 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006280 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006281
6282 out:
6283 free(errmsg);
6284 return err_code;
6285
6286 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006287 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006288 err_code |= ERR_ALERT | ERR_FATAL;
6289 goto out;
6290}
6291
Christopher Faulet811f78c2020-04-01 11:10:27 +02006292
6293/* Parses the "option ssl-hello-chk" proxy keyword */
6294int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6295 const char *file, int line)
6296{
6297 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6298 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6299 *
6300 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6301 */
6302 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006303 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006304 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6305 "0079" /* ContentLength : 0x79 bytes after this one */
6306 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6307 "000075" /* HandshakeLength : 0x75 bytes after this one */
6308 "0300" /* Hello Version : 0x0300 = v3 */
6309 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6310 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6311 "00" /* Session ID length : empty (no session ID) */
6312 "004E" /* Cipher Suite Length : 78 bytes after this one */
6313 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6314 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6315 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6316 "000D" "000E" "000F" "0010" /* various bit lengths, */
6317 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6318 "0015" "0016" "0017" "0018"
6319 "0019" "001A" "001B" "002F"
6320 "0030" "0031" "0032" "0033"
6321 "0034" "0035" "0036" "0037"
6322 "0038" "0039" "003A"
6323 "01" /* Compression Length : 0x01 = 1 byte for types */
6324 "00" /* Compression Type : 0x00 = NULL compression */
6325 };
6326
6327 struct tcpcheck_ruleset *rs = NULL;
6328 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6329 struct tcpcheck_rule *chk;
6330 char *errmsg = NULL;
6331 int err_code = 0;
6332
6333 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6334 err_code |= ERR_WARN;
6335
6336 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6337 goto out;
6338
Christopher Faulet811f78c2020-04-01 11:10:27 +02006339 curpx->options2 &= ~PR_O2_CHK_ANY;
6340 curpx->options2 |= PR_O2_TCPCHK_CHK;
6341
6342 free_tcpcheck_vars(&rules->preset_vars);
6343 rules->list = NULL;
6344 rules->flags = 0;
6345
Christopher Faulet61cc8522020-04-20 14:54:42 +02006346 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006347 if (rs)
6348 goto ruleset_found;
6349
Christopher Faulet61cc8522020-04-20 14:54:42 +02006350 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006351 if (rs == NULL) {
6352 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6353 goto error;
6354 }
6355
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006356 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006357 1, curpx, &rs->rules, file, line, &errmsg);
6358 if (!chk) {
6359 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6360 goto error;
6361 }
6362 chk->index = 0;
6363 LIST_ADDQ(&rs->rules, &chk->list);
6364
6365 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006366 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006367 "error-status", "L6RSP", "tout-status", "L6TOUT",
6368 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006369 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006370 if (!chk) {
6371 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6372 goto error;
6373 }
6374 chk->index = 1;
6375 LIST_ADDQ(&rs->rules, &chk->list);
6376
Christopher Faulet811f78c2020-04-01 11:10:27 +02006377 ruleset_found:
6378 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006379 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006380
6381 out:
6382 free(errmsg);
6383 return err_code;
6384
6385 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006386 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006387 err_code |= ERR_ALERT | ERR_FATAL;
6388 goto out;
6389}
6390
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006391/* Parses the "option smtpchk" proxy keyword */
6392int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6393 const char *file, int line)
6394{
6395 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6396
6397 struct tcpcheck_ruleset *rs = NULL;
6398 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6399 struct tcpcheck_rule *chk;
6400 struct tcpcheck_var *var = NULL;
6401 char *cmd = NULL, *errmsg = NULL;
6402 int err_code = 0;
6403
6404 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6405 err_code |= ERR_WARN;
6406
6407 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6408 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006409
6410 curpx->options2 &= ~PR_O2_CHK_ANY;
6411 curpx->options2 |= PR_O2_TCPCHK_CHK;
6412
6413 free_tcpcheck_vars(&rules->preset_vars);
6414 rules->list = NULL;
6415 rules->flags = 0;
6416
6417 cur_arg += 2;
6418 if (*args[cur_arg] && *args[cur_arg+1] &&
6419 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6420 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6421 if (cmd)
6422 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6423 }
6424 else {
6425 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6426 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6427 cmd = strdup("HELO localhost");
6428 }
6429
Christopher Fauletb61caf42020-04-21 10:57:42 +02006430 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006431 if (cmd == NULL || var == NULL) {
6432 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6433 goto error;
6434 }
6435 var->data.type = SMP_T_STR;
6436 var->data.u.str.area = cmd;
6437 var->data.u.str.data = strlen(cmd);
6438 LIST_INIT(&var->list);
6439 LIST_ADDQ(&rules->preset_vars, &var->list);
6440 cmd = NULL;
6441 var = NULL;
6442
Christopher Faulet61cc8522020-04-20 14:54:42 +02006443 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006444 if (rs)
6445 goto ruleset_found;
6446
Christopher Faulet61cc8522020-04-20 14:54:42 +02006447 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006448 if (rs == NULL) {
6449 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6450 goto error;
6451 }
6452
6453 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6454 1, curpx, &rs->rules, file, line, &errmsg);
6455 if (!chk) {
6456 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6457 goto error;
6458 }
6459 chk->index = 0;
6460 LIST_ADDQ(&rs->rules, &chk->list);
6461
6462 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6463 "min-recv", "4",
6464 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006465 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006466 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006467 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006468 if (!chk) {
6469 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6470 goto error;
6471 }
6472 chk->index = 1;
6473 LIST_ADDQ(&rs->rules, &chk->list);
6474
6475 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6476 "min-recv", "4",
6477 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006478 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6479 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006480 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006481 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006482 if (!chk) {
6483 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6484 goto error;
6485 }
6486 chk->index = 2;
6487 LIST_ADDQ(&rs->rules, &chk->list);
6488
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006489 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006490 1, curpx, &rs->rules, file, line, &errmsg);
6491 if (!chk) {
6492 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6493 goto error;
6494 }
6495 chk->index = 3;
6496 LIST_ADDQ(&rs->rules, &chk->list);
6497
6498 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6499 "min-recv", "4",
6500 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006501 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6502 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6503 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006504 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006505 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006506 if (!chk) {
6507 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6508 goto error;
6509 }
6510 chk->index = 4;
6511 LIST_ADDQ(&rs->rules, &chk->list);
6512
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006513 ruleset_found:
6514 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006515 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006516
6517 out:
6518 free(errmsg);
6519 return err_code;
6520
6521 error:
6522 free(cmd);
6523 free(var);
6524 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006525 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006526 err_code |= ERR_ALERT | ERR_FATAL;
6527 goto out;
6528}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006529
Christopher Fauletce355072020-04-02 11:44:39 +02006530/* Parses the "option pgsql-check" proxy keyword */
6531int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6532 const char *file, int line)
6533{
6534 static char pgsql_req[] = {
6535 "%[var(check.plen),htonl,hex]" /* The packet length*/
6536 "00030000" /* the version 3.0 */
6537 "7573657200" /* "user" key */
6538 "%[var(check.username),hex]00" /* the username */
6539 "00"
6540 };
6541
6542 struct tcpcheck_ruleset *rs = NULL;
6543 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6544 struct tcpcheck_rule *chk;
6545 struct tcpcheck_var *var = NULL;
6546 char *user = NULL, *errmsg = NULL;
6547 size_t packetlen = 0;
6548 int err_code = 0;
6549
6550 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6551 err_code |= ERR_WARN;
6552
6553 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6554 goto out;
6555
Christopher Fauletce355072020-04-02 11:44:39 +02006556 curpx->options2 &= ~PR_O2_CHK_ANY;
6557 curpx->options2 |= PR_O2_TCPCHK_CHK;
6558
6559 free_tcpcheck_vars(&rules->preset_vars);
6560 rules->list = NULL;
6561 rules->flags = 0;
6562
6563 cur_arg += 2;
6564 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6565 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6566 file, line, args[0], args[1]);
6567 goto error;
6568 }
6569 if (strcmp(args[cur_arg], "user") == 0) {
6570 packetlen = 15 + strlen(args[cur_arg+1]);
6571 user = strdup(args[cur_arg+1]);
6572
Christopher Fauletb61caf42020-04-21 10:57:42 +02006573 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006574 if (user == NULL || var == NULL) {
6575 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6576 goto error;
6577 }
6578 var->data.type = SMP_T_STR;
6579 var->data.u.str.area = user;
6580 var->data.u.str.data = strlen(user);
6581 LIST_INIT(&var->list);
6582 LIST_ADDQ(&rules->preset_vars, &var->list);
6583 user = NULL;
6584 var = NULL;
6585
Christopher Fauletb61caf42020-04-21 10:57:42 +02006586 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006587 if (var == NULL) {
6588 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6589 goto error;
6590 }
6591 var->data.type = SMP_T_SINT;
6592 var->data.u.sint = packetlen;
6593 LIST_INIT(&var->list);
6594 LIST_ADDQ(&rules->preset_vars, &var->list);
6595 var = NULL;
6596 }
6597 else {
6598 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6599 file, line, args[0], args[1]);
6600 goto error;
6601 }
6602
Christopher Faulet61cc8522020-04-20 14:54:42 +02006603 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006604 if (rs)
6605 goto ruleset_found;
6606
Christopher Faulet61cc8522020-04-20 14:54:42 +02006607 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006608 if (rs == NULL) {
6609 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6610 goto error;
6611 }
6612
6613 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6614 1, curpx, &rs->rules, file, line, &errmsg);
6615 if (!chk) {
6616 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6617 goto error;
6618 }
6619 chk->index = 0;
6620 LIST_ADDQ(&rs->rules, &chk->list);
6621
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006622 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006623 1, curpx, &rs->rules, file, line, &errmsg);
6624 if (!chk) {
6625 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6626 goto error;
6627 }
6628 chk->index = 1;
6629 LIST_ADDQ(&rs->rules, &chk->list);
6630
6631 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6632 "min-recv", "5",
6633 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006634 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006635 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006636 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006637 if (!chk) {
6638 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6639 goto error;
6640 }
6641 chk->index = 2;
6642 LIST_ADDQ(&rs->rules, &chk->list);
6643
Christopher Fauletb841c742020-04-27 18:29:49 +02006644 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 +02006645 "min-recv", "9",
6646 "error-status", "L7STS",
6647 "on-success", "PostgreSQL server is ok",
6648 "on-error", "PostgreSQL unknown error",
6649 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006650 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006651 if (!chk) {
6652 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6653 goto error;
6654 }
6655 chk->index = 3;
6656 LIST_ADDQ(&rs->rules, &chk->list);
6657
Christopher Fauletce355072020-04-02 11:44:39 +02006658 ruleset_found:
6659 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006660 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006661
6662 out:
6663 free(errmsg);
6664 return err_code;
6665
6666 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006667 free(user);
6668 free(var);
6669 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006670 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006671 err_code |= ERR_ALERT | ERR_FATAL;
6672 goto out;
6673}
6674
6675
6676/* Parses the "option mysql-check" proxy keyword */
6677int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6678 const char *file, int line)
6679{
6680 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6681 * const char mysql40_client_auth_pkt[] = {
6682 * "\x0e\x00\x00" // packet length
6683 * "\x01" // packet number
6684 * "\x00\x00" // client capabilities
6685 * "\x00\x00\x01" // max packet
6686 * "haproxy\x00" // username (null terminated string)
6687 * "\x00" // filler (always 0x00)
6688 * "\x01\x00\x00" // packet length
6689 * "\x00" // packet number
6690 * "\x01" // COM_QUIT command
6691 * };
6692 */
6693 static char mysql40_rsname[] = "*mysql40-check";
6694 static char mysql40_req[] = {
6695 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6696 "0080" /* client capabilities */
6697 "000001" /* max packet */
6698 "%[var(check.username),hex]00" /* the username */
6699 "00" /* filler (always 0x00) */
6700 "010000" /* packet length*/
6701 "00" /* sequence ID */
6702 "01" /* COM_QUIT command */
6703 };
6704
6705 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6706 * const char mysql41_client_auth_pkt[] = {
6707 * "\x0e\x00\x00\" // packet length
6708 * "\x01" // packet number
6709 * "\x00\x00\x00\x00" // client capabilities
6710 * "\x00\x00\x00\x01" // max packet
6711 * "\x21" // character set (UTF-8)
6712 * char[23] // All zeroes
6713 * "haproxy\x00" // username (null terminated string)
6714 * "\x00" // filler (always 0x00)
6715 * "\x01\x00\x00" // packet length
6716 * "\x00" // packet number
6717 * "\x01" // COM_QUIT command
6718 * };
6719 */
6720 static char mysql41_rsname[] = "*mysql41-check";
6721 static char mysql41_req[] = {
6722 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6723 "00820000" /* client capabilities */
6724 "00800001" /* max packet */
6725 "21" /* character set (UTF-8) */
6726 "000000000000000000000000" /* 23 bytes, al zeroes */
6727 "0000000000000000000000"
6728 "%[var(check.username),hex]00" /* the username */
6729 "00" /* filler (always 0x00) */
6730 "010000" /* packet length*/
6731 "00" /* sequence ID */
6732 "01" /* COM_QUIT command */
6733 };
6734
6735 struct tcpcheck_ruleset *rs = NULL;
6736 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6737 struct tcpcheck_rule *chk;
6738 struct tcpcheck_var *var = NULL;
6739 char *mysql_rsname = "*mysql-check";
6740 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6741 int index = 0, err_code = 0;
6742
6743 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6744 err_code |= ERR_WARN;
6745
6746 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6747 goto out;
6748
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006749 curpx->options2 &= ~PR_O2_CHK_ANY;
6750 curpx->options2 |= PR_O2_TCPCHK_CHK;
6751
6752 free_tcpcheck_vars(&rules->preset_vars);
6753 rules->list = NULL;
6754 rules->flags = 0;
6755
6756 cur_arg += 2;
6757 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006758 int packetlen, userlen;
6759
6760 if (strcmp(args[cur_arg], "user") != 0) {
6761 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6762 file, line, args[0], args[1], args[cur_arg]);
6763 goto error;
6764 }
6765
6766 if (*(args[cur_arg+1]) == 0) {
6767 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6768 file, line, args[0], args[1], args[cur_arg]);
6769 goto error;
6770 }
6771
6772 hdr = calloc(4, sizeof(*hdr));
6773 user = strdup(args[cur_arg+1]);
6774 userlen = strlen(args[cur_arg+1]);
6775
6776 if (hdr == NULL || user == NULL) {
6777 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6778 goto error;
6779 }
6780
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006781 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006782 packetlen = userlen + 7 + 27;
6783 mysql_req = mysql41_req;
6784 mysql_rsname = mysql41_rsname;
6785 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006786 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006787 packetlen = userlen + 7;
6788 mysql_req = mysql40_req;
6789 mysql_rsname = mysql40_rsname;
6790 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006791 else {
6792 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
6793 file, line, args[cur_arg], args[cur_arg+2]);
6794 goto error;
6795 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006796
6797 hdr[0] = (unsigned char)(packetlen & 0xff);
6798 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6799 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6800 hdr[3] = 1;
6801
Christopher Fauletb61caf42020-04-21 10:57:42 +02006802 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006803 if (var == NULL) {
6804 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6805 goto error;
6806 }
6807 var->data.type = SMP_T_STR;
6808 var->data.u.str.area = hdr;
6809 var->data.u.str.data = 4;
6810 LIST_INIT(&var->list);
6811 LIST_ADDQ(&rules->preset_vars, &var->list);
6812 hdr = NULL;
6813 var = NULL;
6814
Christopher Fauletb61caf42020-04-21 10:57:42 +02006815 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006816 if (var == NULL) {
6817 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6818 goto error;
6819 }
6820 var->data.type = SMP_T_STR;
6821 var->data.u.str.area = user;
6822 var->data.u.str.data = strlen(user);
6823 LIST_INIT(&var->list);
6824 LIST_ADDQ(&rules->preset_vars, &var->list);
6825 user = NULL;
6826 var = NULL;
6827 }
6828
Christopher Faulet61cc8522020-04-20 14:54:42 +02006829 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006830 if (rs)
6831 goto ruleset_found;
6832
Christopher Faulet61cc8522020-04-20 14:54:42 +02006833 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006834 if (rs == NULL) {
6835 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6836 goto error;
6837 }
6838
6839 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6840 1, curpx, &rs->rules, file, line, &errmsg);
6841 if (!chk) {
6842 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6843 goto error;
6844 }
6845 chk->index = index++;
6846 LIST_ADDQ(&rs->rules, &chk->list);
6847
6848 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006849 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006850 1, curpx, &rs->rules, file, line, &errmsg);
6851 if (!chk) {
6852 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6853 goto error;
6854 }
6855 chk->index = index++;
6856 LIST_ADDQ(&rs->rules, &chk->list);
6857 }
6858
6859 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006860 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006861 if (!chk) {
6862 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6863 goto error;
6864 }
6865 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6866 chk->index = index++;
6867 LIST_ADDQ(&rs->rules, &chk->list);
6868
6869 if (mysql_req) {
6870 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006871 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006872 if (!chk) {
6873 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6874 goto error;
6875 }
6876 chk->expect.custom = tcpcheck_mysql_expect_ok;
6877 chk->index = index++;
6878 LIST_ADDQ(&rs->rules, &chk->list);
6879 }
6880
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006881 ruleset_found:
6882 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006883 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006884
6885 out:
6886 free(errmsg);
6887 return err_code;
6888
6889 error:
6890 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006891 free(user);
6892 free(var);
6893 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006894 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006895 err_code |= ERR_ALERT | ERR_FATAL;
6896 goto out;
6897}
6898
Christopher Faulet1997eca2020-04-03 23:13:50 +02006899int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6900 const char *file, int line)
6901{
6902 static char *ldap_req = "300C020101600702010304008000";
6903
6904 struct tcpcheck_ruleset *rs = NULL;
6905 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6906 struct tcpcheck_rule *chk;
6907 char *errmsg = NULL;
6908 int err_code = 0;
6909
6910 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6911 err_code |= ERR_WARN;
6912
6913 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6914 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006915
6916 curpx->options2 &= ~PR_O2_CHK_ANY;
6917 curpx->options2 |= PR_O2_TCPCHK_CHK;
6918
6919 free_tcpcheck_vars(&rules->preset_vars);
6920 rules->list = NULL;
6921 rules->flags = 0;
6922
Christopher Faulet61cc8522020-04-20 14:54:42 +02006923 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006924 if (rs)
6925 goto ruleset_found;
6926
Christopher Faulet61cc8522020-04-20 14:54:42 +02006927 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006928 if (rs == NULL) {
6929 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6930 goto error;
6931 }
6932
6933 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6934 1, curpx, &rs->rules, file, line, &errmsg);
6935 if (!chk) {
6936 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6937 goto error;
6938 }
6939 chk->index = 0;
6940 LIST_ADDQ(&rs->rules, &chk->list);
6941
6942 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6943 "min-recv", "14",
6944 "on-error", "Not LDAPv3 protocol",
6945 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006946 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006947 if (!chk) {
6948 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6949 goto error;
6950 }
6951 chk->index = 1;
6952 LIST_ADDQ(&rs->rules, &chk->list);
6953
6954 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006955 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006956 if (!chk) {
6957 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6958 goto error;
6959 }
6960 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6961 chk->index = 2;
6962 LIST_ADDQ(&rs->rules, &chk->list);
6963
Christopher Faulet1997eca2020-04-03 23:13:50 +02006964 ruleset_found:
6965 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006966 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006967
6968 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006969 free(errmsg);
6970 return err_code;
6971
6972 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006973 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006974 err_code |= ERR_ALERT | ERR_FATAL;
6975 goto out;
6976}
6977
6978int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6979 const char *file, int line)
6980{
6981 struct tcpcheck_ruleset *rs = NULL;
6982 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6983 struct tcpcheck_rule *chk;
6984 char *spop_req = NULL;
6985 char *errmsg = NULL;
6986 int spop_len = 0, err_code = 0;
6987
6988 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6989 err_code |= ERR_WARN;
6990
6991 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6992 goto out;
6993
Christopher Faulet267b01b2020-04-04 10:27:09 +02006994 curpx->options2 &= ~PR_O2_CHK_ANY;
6995 curpx->options2 |= PR_O2_TCPCHK_CHK;
6996
6997 free_tcpcheck_vars(&rules->preset_vars);
6998 rules->list = NULL;
6999 rules->flags = 0;
7000
7001
Christopher Faulet61cc8522020-04-20 14:54:42 +02007002 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007003 if (rs)
7004 goto ruleset_found;
7005
Christopher Faulet61cc8522020-04-20 14:54:42 +02007006 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007007 if (rs == NULL) {
7008 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7009 goto error;
7010 }
7011
7012 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7013 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7014 goto error;
7015 }
7016 chunk_reset(&trash);
7017 dump_binary(&trash, spop_req, spop_len);
7018 trash.area[trash.data] = '\0';
7019
7020 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7021 1, curpx, &rs->rules, file, line, &errmsg);
7022 if (!chk) {
7023 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7024 goto error;
7025 }
7026 chk->index = 0;
7027 LIST_ADDQ(&rs->rules, &chk->list);
7028
7029 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007030 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007031 if (!chk) {
7032 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7033 goto error;
7034 }
7035 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7036 chk->index = 1;
7037 LIST_ADDQ(&rs->rules, &chk->list);
7038
Christopher Faulet267b01b2020-04-04 10:27:09 +02007039 ruleset_found:
7040 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007041 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007042
7043 out:
7044 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007045 free(errmsg);
7046 return err_code;
7047
7048 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007049 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007050 err_code |= ERR_ALERT | ERR_FATAL;
7051 goto out;
7052}
Christopher Fauletce355072020-04-02 11:44:39 +02007053
Christopher Faulete5870d82020-04-15 11:32:03 +02007054
7055struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7056{
7057 struct tcpcheck_rule *chk = NULL;
7058 struct tcpcheck_http_hdr *hdr = NULL;
7059 char *meth = NULL, *uri = NULL, *vsn = NULL;
7060 char *hdrs, *body;
7061
7062 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7063 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7064 if (hdrs == body)
7065 hdrs = NULL;
7066 if (hdrs) {
7067 *hdrs = '\0';
7068 hdrs +=2;
7069 }
7070 if (body) {
7071 *body = '\0';
7072 body += 4;
7073 }
7074 if (hdrs || body) {
7075 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7076 " Please, consider to use 'http-check send' directive instead.");
7077 }
7078
7079 chk = calloc(1, sizeof(*chk));
7080 if (!chk) {
7081 memprintf(errmsg, "out of memory");
7082 goto error;
7083 }
7084 chk->action = TCPCHK_ACT_SEND;
7085 chk->send.type = TCPCHK_SEND_HTTP;
7086 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7087 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7088 LIST_INIT(&chk->send.http.hdrs);
7089
7090 /* Copy the method, uri and version */
7091 if (*args[cur_arg]) {
7092 if (!*args[cur_arg+1])
7093 uri = args[cur_arg];
7094 else
7095 meth = args[cur_arg];
7096 }
7097 if (*args[cur_arg+1])
7098 uri = args[cur_arg+1];
7099 if (*args[cur_arg+2])
7100 vsn = args[cur_arg+2];
7101
7102 if (meth) {
7103 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7104 chk->send.http.meth.str.area = strdup(meth);
7105 chk->send.http.meth.str.data = strlen(meth);
7106 if (!chk->send.http.meth.str.area) {
7107 memprintf(errmsg, "out of memory");
7108 goto error;
7109 }
7110 }
7111 if (uri) {
7112 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007113 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007114 memprintf(errmsg, "out of memory");
7115 goto error;
7116 }
7117 }
7118 if (vsn) {
7119 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007120 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007121 memprintf(errmsg, "out of memory");
7122 goto error;
7123 }
7124 }
7125
7126 /* Copy the header */
7127 if (hdrs) {
7128 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7129 struct h1m h1m;
7130 int i, ret;
7131
7132 /* Build and parse the request */
7133 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7134
7135 h1m.flags = H1_MF_HDRS_ONLY;
7136 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7137 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7138 &h1m, NULL);
7139 if (ret <= 0) {
7140 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7141 goto error;
7142 }
7143
Christopher Fauletb61caf42020-04-21 10:57:42 +02007144 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007145 hdr = calloc(1, sizeof(*hdr));
7146 if (!hdr) {
7147 memprintf(errmsg, "out of memory");
7148 goto error;
7149 }
7150 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007151 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007152 if (!hdr->name.ptr) {
7153 memprintf(errmsg, "out of memory");
7154 goto error;
7155 }
7156
Christopher Fauletb61caf42020-04-21 10:57:42 +02007157 ist0(tmp_hdrs[i].v);
7158 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 +02007159 goto error;
7160 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7161 }
7162 }
7163
7164 /* Copy the body */
7165 if (body) {
7166 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007167 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007168 memprintf(errmsg, "out of memory");
7169 goto error;
7170 }
7171 }
7172
7173 return chk;
7174
7175 error:
7176 free_tcpcheck_http_hdr(hdr);
7177 free_tcpcheck(chk, 0);
7178 return NULL;
7179}
7180
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007181int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7182 const char *file, int line)
7183{
Christopher Faulete5870d82020-04-15 11:32:03 +02007184 struct tcpcheck_ruleset *rs = NULL;
7185 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7186 struct tcpcheck_rule *chk;
7187 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007188 int err_code = 0;
7189
7190 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7191 err_code |= ERR_WARN;
7192
7193 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7194 goto out;
7195
Christopher Faulete5870d82020-04-15 11:32:03 +02007196 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7197 if (!chk) {
7198 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7199 goto error;
7200 }
7201 if (errmsg) {
7202 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7203 err_code |= ERR_WARN;
7204 free(errmsg);
7205 errmsg = NULL;
7206 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007207
Christopher Faulete5870d82020-04-15 11:32:03 +02007208 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007209 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007210 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007211
Christopher Faulete5870d82020-04-15 11:32:03 +02007212 free_tcpcheck_vars(&rules->preset_vars);
7213 rules->list = NULL;
7214 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007215
Christopher Faulete5870d82020-04-15 11:32:03 +02007216 /* Deduce the ruleset name from the proxy info */
7217 chunk_printf(&trash, "*http-check-%s_%s-%d",
7218 ((curpx == defpx) ? "defaults" : curpx->id),
7219 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007220
Christopher Faulet61cc8522020-04-20 14:54:42 +02007221 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007222 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007223 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007224 if (rs == NULL) {
7225 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7226 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007227 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007228 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007229
Christopher Faulete5870d82020-04-15 11:32:03 +02007230 rules->list = &rs->rules;
7231 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7232 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7233 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7234 rules->list = NULL;
7235 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007236 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007237
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007238 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007239 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007240 return err_code;
7241
7242 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007243 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007244 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007245 err_code |= ERR_ALERT | ERR_FATAL;
7246 goto out;
7247}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007248
Christopher Faulet6f557912020-04-09 15:58:50 +02007249int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7250 const char *file, int line)
7251{
7252 int err_code = 0;
7253
Christopher Faulet6f557912020-04-09 15:58:50 +02007254 curpx->options2 &= ~PR_O2_CHK_ANY;
7255 curpx->options2 |= PR_O2_EXT_CHK;
7256 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7257 goto out;
7258
7259 out:
7260 return err_code;
7261}
7262
Christopher Fauletce8111e2020-04-06 15:04:11 +02007263/* Parse the "addr" server keyword */
7264static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7265 char **errmsg)
7266{
7267 struct sockaddr_storage *sk;
7268 struct protocol *proto;
7269 int port1, port2, err_code = 0;
7270
7271
7272 if (!*args[*cur_arg+1]) {
7273 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7274 goto error;
7275 }
7276
7277 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7278 if (!sk) {
7279 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7280 goto error;
7281 }
7282
7283 proto = protocol_by_family(sk->ss_family);
7284 if (!proto || !proto->connect) {
7285 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7286 args[*cur_arg], args[*cur_arg+1]);
7287 goto error;
7288 }
7289
7290 if (port1 != port2) {
7291 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7292 args[*cur_arg], args[*cur_arg+1]);
7293 goto error;
7294 }
7295
7296 srv->check.addr = srv->agent.addr = *sk;
7297 srv->flags |= SRV_F_CHECKADDR;
7298 srv->flags |= SRV_F_AGENTADDR;
7299
7300 out:
7301 return err_code;
7302
7303 error:
7304 err_code |= ERR_ALERT | ERR_FATAL;
7305 goto out;
7306}
7307
7308
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007309/* Parse the "agent-addr" server keyword */
7310static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7311 char **errmsg)
7312{
7313 int err_code = 0;
7314
7315 if (!*(args[*cur_arg+1])) {
7316 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7317 goto error;
7318 }
7319 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7320 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7321 goto error;
7322 }
7323
7324 out:
7325 return err_code;
7326
7327 error:
7328 err_code |= ERR_ALERT | ERR_FATAL;
7329 goto out;
7330}
7331
7332/* Parse the "agent-check" server keyword */
7333static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7334 char **errmsg)
7335{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007336 struct tcpcheck_ruleset *rs = NULL;
7337 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7338 struct tcpcheck_rule *chk;
7339 int err_code = 0;
7340
7341 if (srv->do_agent)
7342 goto out;
7343
7344 if (!rules) {
7345 rules = calloc(1, sizeof(*rules));
7346 if (!rules) {
7347 memprintf(errmsg, "out of memory.");
7348 goto error;
7349 }
7350 LIST_INIT(&rules->preset_vars);
7351 srv->agent.tcpcheck_rules = rules;
7352 }
7353 rules->list = NULL;
7354 rules->flags = 0;
7355
Christopher Faulet61cc8522020-04-20 14:54:42 +02007356 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007357 if (rs)
7358 goto ruleset_found;
7359
Christopher Faulet61cc8522020-04-20 14:54:42 +02007360 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007361 if (rs == NULL) {
7362 memprintf(errmsg, "out of memory.");
7363 goto error;
7364 }
7365
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007366 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007367 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7368 if (!chk) {
7369 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7370 goto error;
7371 }
7372 chk->index = 0;
7373 LIST_ADDQ(&rs->rules, &chk->list);
7374
7375 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007376 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7377 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007378 if (!chk) {
7379 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7380 goto error;
7381 }
7382 chk->expect.custom = tcpcheck_agent_expect_reply;
7383 chk->index = 1;
7384 LIST_ADDQ(&rs->rules, &chk->list);
7385
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007386 ruleset_found:
7387 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007388 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007389 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007390
7391 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007392 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007393
7394 error:
7395 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007396 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007397 err_code |= ERR_ALERT | ERR_FATAL;
7398 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007399}
7400
7401/* Parse the "agent-inter" server keyword */
7402static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7403 char **errmsg)
7404{
7405 const char *err = NULL;
7406 unsigned int delay;
7407 int err_code = 0;
7408
7409 if (!*(args[*cur_arg+1])) {
7410 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7411 goto error;
7412 }
7413
7414 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7415 if (err == PARSE_TIME_OVER) {
7416 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7417 args[*cur_arg+1], args[*cur_arg], srv->id);
7418 goto error;
7419 }
7420 else if (err == PARSE_TIME_UNDER) {
7421 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7422 args[*cur_arg+1], args[*cur_arg], srv->id);
7423 goto error;
7424 }
7425 else if (err) {
7426 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7427 *err, srv->id);
7428 goto error;
7429 }
7430 if (delay <= 0) {
7431 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7432 delay, args[*cur_arg], srv->id);
7433 goto error;
7434 }
7435 srv->agent.inter = delay;
7436
7437 out:
7438 return err_code;
7439
7440 error:
7441 err_code |= ERR_ALERT | ERR_FATAL;
7442 goto out;
7443}
7444
7445/* Parse the "agent-port" server keyword */
7446static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7447 char **errmsg)
7448{
7449 int err_code = 0;
7450
7451 if (!*(args[*cur_arg+1])) {
7452 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7453 goto error;
7454 }
7455
7456 global.maxsock++;
7457 srv->agent.port = atol(args[*cur_arg+1]);
7458
7459 out:
7460 return err_code;
7461
7462 error:
7463 err_code |= ERR_ALERT | ERR_FATAL;
7464 goto out;
7465}
7466
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007467int set_srv_agent_send(struct server *srv, const char *send)
7468{
7469 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7470 struct tcpcheck_var *var = NULL;
7471 char *str;
7472
7473 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007474 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007475 if (str == NULL || var == NULL)
7476 goto error;
7477
7478 free_tcpcheck_vars(&rules->preset_vars);
7479
7480 var->data.type = SMP_T_STR;
7481 var->data.u.str.area = str;
7482 var->data.u.str.data = strlen(str);
7483 LIST_INIT(&var->list);
7484 LIST_ADDQ(&rules->preset_vars, &var->list);
7485
7486 return 1;
7487
7488 error:
7489 free(str);
7490 free(var);
7491 return 0;
7492}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007493
7494/* Parse the "agent-send" server keyword */
7495static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7496 char **errmsg)
7497{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007498 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007499 int err_code = 0;
7500
7501 if (!*(args[*cur_arg+1])) {
7502 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7503 goto error;
7504 }
7505
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007506 if (!rules) {
7507 rules = calloc(1, sizeof(*rules));
7508 if (!rules) {
7509 memprintf(errmsg, "out of memory.");
7510 goto error;
7511 }
7512 LIST_INIT(&rules->preset_vars);
7513 srv->agent.tcpcheck_rules = rules;
7514 }
7515
7516 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007517 memprintf(errmsg, "out of memory.");
7518 goto error;
7519 }
7520
7521 out:
7522 return err_code;
7523
7524 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007525 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007526 err_code |= ERR_ALERT | ERR_FATAL;
7527 goto out;
7528}
7529
7530/* Parse the "no-agent-send" server keyword */
7531static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7532 char **errmsg)
7533{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007534 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007535 return 0;
7536}
7537
Christopher Fauletce8111e2020-04-06 15:04:11 +02007538/* Parse the "check" server keyword */
7539static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7540 char **errmsg)
7541{
7542 srv->do_check = 1;
7543 return 0;
7544}
7545
7546/* Parse the "check-send-proxy" server keyword */
7547static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7548 char **errmsg)
7549{
7550 srv->check.send_proxy = 1;
7551 return 0;
7552}
7553
7554/* Parse the "check-via-socks4" server keyword */
7555static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7556 char **errmsg)
7557{
7558 srv->check.via_socks4 = 1;
7559 return 0;
7560}
7561
7562/* Parse the "no-check" server keyword */
7563static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7564 char **errmsg)
7565{
7566 deinit_srv_check(srv);
7567 return 0;
7568}
7569
7570/* Parse the "no-check-send-proxy" server keyword */
7571static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7572 char **errmsg)
7573{
7574 srv->check.send_proxy = 0;
7575 return 0;
7576}
7577
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007578/* parse the "check-proto" server keyword */
7579static int srv_parse_check_proto(char **args, int *cur_arg,
7580 struct proxy *px, struct server *newsrv, char **err)
7581{
7582 int err_code = 0;
7583
7584 if (!*args[*cur_arg + 1]) {
7585 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7586 goto error;
7587 }
7588 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7589 if (!newsrv->check.mux_proto) {
7590 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7591 goto error;
7592 }
7593
7594 out:
7595 return err_code;
7596
7597 error:
7598 err_code |= ERR_ALERT | ERR_FATAL;
7599 goto out;
7600}
7601
7602
Christopher Fauletce8111e2020-04-06 15:04:11 +02007603/* Parse the "rise" server keyword */
7604static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7605 char **errmsg)
7606{
7607 int err_code = 0;
7608
7609 if (!*args[*cur_arg + 1]) {
7610 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7611 goto error;
7612 }
7613
7614 srv->check.rise = atol(args[*cur_arg+1]);
7615 if (srv->check.rise <= 0) {
7616 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7617 goto error;
7618 }
7619
7620 if (srv->check.health)
7621 srv->check.health = srv->check.rise;
7622
7623 out:
7624 return err_code;
7625
7626 error:
7627 deinit_srv_agent_check(srv);
7628 err_code |= ERR_ALERT | ERR_FATAL;
7629 goto out;
7630 return 0;
7631}
7632
7633/* Parse the "fall" server keyword */
7634static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7635 char **errmsg)
7636{
7637 int err_code = 0;
7638
7639 if (!*args[*cur_arg + 1]) {
7640 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7641 goto error;
7642 }
7643
7644 srv->check.fall = atol(args[*cur_arg+1]);
7645 if (srv->check.fall <= 0) {
7646 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7647 goto error;
7648 }
7649
7650 out:
7651 return err_code;
7652
7653 error:
7654 deinit_srv_agent_check(srv);
7655 err_code |= ERR_ALERT | ERR_FATAL;
7656 goto out;
7657 return 0;
7658}
7659
7660/* Parse the "inter" server keyword */
7661static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7662 char **errmsg)
7663{
7664 const char *err = NULL;
7665 unsigned int delay;
7666 int err_code = 0;
7667
7668 if (!*(args[*cur_arg+1])) {
7669 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7670 goto error;
7671 }
7672
7673 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7674 if (err == PARSE_TIME_OVER) {
7675 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7676 args[*cur_arg+1], args[*cur_arg], srv->id);
7677 goto error;
7678 }
7679 else if (err == PARSE_TIME_UNDER) {
7680 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7681 args[*cur_arg+1], args[*cur_arg], srv->id);
7682 goto error;
7683 }
7684 else if (err) {
7685 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7686 *err, srv->id);
7687 goto error;
7688 }
7689 if (delay <= 0) {
7690 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7691 delay, args[*cur_arg], srv->id);
7692 goto error;
7693 }
7694 srv->check.inter = delay;
7695
7696 out:
7697 return err_code;
7698
7699 error:
7700 err_code |= ERR_ALERT | ERR_FATAL;
7701 goto out;
7702}
7703
7704
7705/* Parse the "fastinter" server keyword */
7706static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7707 char **errmsg)
7708{
7709 const char *err = NULL;
7710 unsigned int delay;
7711 int err_code = 0;
7712
7713 if (!*(args[*cur_arg+1])) {
7714 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7715 goto error;
7716 }
7717
7718 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7719 if (err == PARSE_TIME_OVER) {
7720 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7721 args[*cur_arg+1], args[*cur_arg], srv->id);
7722 goto error;
7723 }
7724 else if (err == PARSE_TIME_UNDER) {
7725 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7726 args[*cur_arg+1], args[*cur_arg], srv->id);
7727 goto error;
7728 }
7729 else if (err) {
7730 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7731 *err, srv->id);
7732 goto error;
7733 }
7734 if (delay <= 0) {
7735 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7736 delay, args[*cur_arg], srv->id);
7737 goto error;
7738 }
7739 srv->check.fastinter = delay;
7740
7741 out:
7742 return err_code;
7743
7744 error:
7745 err_code |= ERR_ALERT | ERR_FATAL;
7746 goto out;
7747}
7748
7749
7750/* Parse the "downinter" server keyword */
7751static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7752 char **errmsg)
7753{
7754 const char *err = NULL;
7755 unsigned int delay;
7756 int err_code = 0;
7757
7758 if (!*(args[*cur_arg+1])) {
7759 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7760 goto error;
7761 }
7762
7763 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7764 if (err == PARSE_TIME_OVER) {
7765 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7766 args[*cur_arg+1], args[*cur_arg], srv->id);
7767 goto error;
7768 }
7769 else if (err == PARSE_TIME_UNDER) {
7770 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7771 args[*cur_arg+1], args[*cur_arg], srv->id);
7772 goto error;
7773 }
7774 else if (err) {
7775 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7776 *err, srv->id);
7777 goto error;
7778 }
7779 if (delay <= 0) {
7780 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7781 delay, args[*cur_arg], srv->id);
7782 goto error;
7783 }
7784 srv->check.downinter = delay;
7785
7786 out:
7787 return err_code;
7788
7789 error:
7790 err_code |= ERR_ALERT | ERR_FATAL;
7791 goto out;
7792}
7793
7794/* Parse the "port" server keyword */
7795static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7796 char **errmsg)
7797{
7798 int err_code = 0;
7799
7800 if (!*(args[*cur_arg+1])) {
7801 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7802 goto error;
7803 }
7804
7805 global.maxsock++;
7806 srv->check.port = atol(args[*cur_arg+1]);
7807 srv->flags |= SRV_F_CHECKPORT;
7808
7809 out:
7810 return err_code;
7811
7812 error:
7813 err_code |= ERR_ALERT | ERR_FATAL;
7814 goto out;
7815}
7816
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007817static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007818 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7819 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7820 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007821 { 0, NULL, NULL },
7822}};
7823
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007824static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007825 { "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 +02007826 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7827 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7828 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7829 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7830 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007831 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007832 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007833 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7834 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007835 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007836 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7837 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7838 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7839 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7840 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7841 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7842 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7843 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007844 { NULL, NULL, 0 },
7845}};
7846
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007847INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007848INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007849
Willy Tarreaubd741542010-03-16 18:46:54 +01007850/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007851 * Local variables:
7852 * c-indent-level: 8
7853 * c-basic-offset: 8
7854 * End:
7855 */