blob: edb4fea4b8a7455166d1779e1123df2f54c24d1d [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 Tarreau3727a8a2020-06-04 17:37:26 +020049#include <haproxy/signal.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020050#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020051#include <haproxy/stats-t.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020052#include <haproxy/task.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020053#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020054
Willy Tarreauf268ee82020-06-04 17:05:57 +020055#include <haproxy/global.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020056
Willy Tarreauaa74c4e2020-06-04 10:19:23 +020057#include <haproxy/arg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020058#include <proto/backend.h>
Willy Tarreau0f6ffd62020-06-03 19:33:00 +020059#include <haproxy/fd.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020060#include <proto/log.h>
61#include <proto/queue.h>
Willy Tarreaufc8f6a82020-06-03 19:20:59 +020062#include <haproxy/port_range.h>
Willy Tarreaufc774542020-06-04 17:31:04 +020063#include <haproxy/proto_tcp.h>
Willy Tarreau2dd7c352020-06-03 15:26:55 +020064#include <haproxy/protocol.h>
Willy Tarreau2b5652f2006-12-31 17:46:05 +010065#include <proto/proxy.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020066#include <proto/server.h>
Willy Tarreau9e000c62011-03-10 14:03:36 +010067#include <proto/stream_interface.h>
Baptiste Assmanna68ca962015-04-14 01:15:08 +020068#include <proto/log.h>
Willy Tarreau832ce652020-06-04 08:36:05 +020069#include <haproxy/proto_udp.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020070#include <haproxy/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020071
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +020072static int tcpcheck_get_step_id(struct check *, struct tcpcheck_rule *);
Christopher Faulet31c30fd2020-03-26 21:10:03 +010073
Christopher Faulet61cc8522020-04-20 14:54:42 +020074static int wake_srv_chk(struct conn_stream *cs);
75struct data_cb check_conn_cb = {
76 .wake = wake_srv_chk,
77 .name = "CHCK",
78};
Christopher Fauletd7e63962020-04-17 20:15:59 +020079
Christopher Fauletd7cee712020-04-21 13:45:00 +020080/* Global tree to share all tcp-checks */
81struct eb_root shared_tcpchecks = EB_ROOT;
Christopher Faulet5d503fc2020-03-30 20:34:34 +020082
83
Willy Tarreau8ceae722018-11-26 11:58:30 +010084DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
85DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
Christopher Faulet31dff9b2017-10-23 15:45:20 +020086
Gaetan Rivet05d692d2020-02-14 17:42:54 +010087/* Dummy frontend used to create all checks sessions. */
88static struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020089
Christopher Faulet61cc8522020-04-20 14:54:42 +020090/**************************************************************************/
91/************************ Handle check results ****************************/
92/**************************************************************************/
93struct check_status {
94 short result; /* one of SRV_CHK_* */
95 char *info; /* human readable short info */
96 char *desc; /* long description */
97};
98
99struct analyze_status {
100 char *desc; /* description */
101 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
102};
103
Simon Horman63a4a822012-03-19 07:24:41 +0900104static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100105 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
106 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200107 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200108
Willy Tarreau23964182014-05-20 20:56:30 +0200109 /* Below we have finished checks */
110 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100111 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100112
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100113 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200114
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100115 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
116 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
117 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200118
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100119 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
120 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
121 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200122
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100123 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
124 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200125
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200126 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200127
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100128 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
129 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
130 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900131
132 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
133 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200134 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200135};
136
Simon Horman63a4a822012-03-19 07:24:41 +0900137static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100138 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
139
140 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
141 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
142
143 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
144 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
145 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
146 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
147
148 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
149 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
150 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
151};
152
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100153/* checks if <err> is a real error for errno or one that can be ignored, and
154 * return 0 for these ones or <err> for real ones.
155 */
156static inline int unclean_errno(int err)
157{
158 if (err == EAGAIN || err == EINPROGRESS ||
159 err == EISCONN || err == EALREADY)
160 return 0;
161 return err;
162}
163
Christopher Faulet61cc8522020-04-20 14:54:42 +0200164/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200165const char *get_check_status_description(short check_status) {
166
167 const char *desc;
168
169 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200170 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200171 else
172 desc = NULL;
173
174 if (desc && *desc)
175 return desc;
176 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200177 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200178}
179
Christopher Faulet61cc8522020-04-20 14:54:42 +0200180/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200181const char *get_check_status_info(short check_status) {
182
183 const char *info;
184
185 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200186 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200187 else
188 info = NULL;
189
190 if (info && *info)
191 return info;
192 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200193 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200194}
195
Christopher Faulet61cc8522020-04-20 14:54:42 +0200196/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100197const char *get_analyze_status(short analyze_status) {
198
199 const char *desc;
200
201 if (analyze_status < HANA_STATUS_SIZE)
202 desc = analyze_statuses[analyze_status].desc;
203 else
204 desc = NULL;
205
206 if (desc && *desc)
207 return desc;
208 else
209 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
210}
211
Christopher Faulet61cc8522020-04-20 14:54:42 +0200212/* Sets check->status, update check->duration and fill check->result with an
213 * adequate CHK_RES_* value. The new check->health is computed based on the
214 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200215 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200216 * Shows information in logs about failed health check if server is UP or
217 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200218 */
Simon Horman4a741432013-02-23 15:35:38 +0900219static void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100220{
Simon Horman4a741432013-02-23 15:35:38 +0900221 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200222 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200223 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900224
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200225 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100226 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900227 check->desc[0] = '\0';
228 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200229 return;
230 }
231
Simon Horman4a741432013-02-23 15:35:38 +0900232 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200233 return;
234
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200235 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900236 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
237 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200238 } else
Simon Horman4a741432013-02-23 15:35:38 +0900239 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200240
Simon Horman4a741432013-02-23 15:35:38 +0900241 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200242 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900243 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200244
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100245 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900246 check->duration = -1;
247 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200248 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900249 check->duration = tv_ms_elapsed(&check->start, &now);
250 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200251 }
252
Willy Tarreau23964182014-05-20 20:56:30 +0200253 /* no change is expected if no state change occurred */
254 if (check->result == CHK_RES_NEUTRAL)
255 return;
256
Olivier Houchard0923fa42019-01-11 18:43:04 +0100257 /* If the check was really just sending a mail, it won't have an
258 * associated server, so we're done now.
259 */
260 if (!s)
261 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200262 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200263
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 switch (check->result) {
265 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200266 /* Failure to connect to the agent as a secondary check should not
267 * cause the server to be marked down.
268 */
269 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900270 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200271 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100272 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200273 report = 1;
274 check->health--;
275 if (check->health < check->rise)
276 check->health = 0;
277 }
278 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200279
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200280 case CHK_RES_PASSED:
281 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
282 if ((check->health < check->rise + check->fall - 1) &&
283 (check->result == CHK_RES_PASSED || check->health > 0)) {
284 report = 1;
285 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200286
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200287 if (check->health >= check->rise)
288 check->health = check->rise + check->fall - 1; /* OK now */
289 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200290
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200291 /* clear consecutive_errors if observing is enabled */
292 if (s->onerror)
293 s->consecutive_errors = 0;
294 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100295
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200296 default:
297 break;
298 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200299
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200300 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
301 (status != prev_status || report)) {
302 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200303 "%s check for %sserver %s/%s %s%s",
304 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200305 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100306 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100307 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200308 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200309
Emeric Brun5a133512017-10-19 14:42:30 +0200310 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100312 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200313 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
314 (check->health >= check->rise) ? check->fall : check->rise,
315 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200316
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200317 ha_warning("%s.\n", trash.area);
318 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
319 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200320 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200321}
322
Willy Tarreau4eec5472014-05-20 22:32:27 +0200323/* Marks the check <check>'s server down if the current check is already failed
324 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200325 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200326static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200327{
Simon Horman4a741432013-02-23 15:35:38 +0900328 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900329
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200330 /* The agent secondary check should only cause a server to be marked
331 * as down if check->status is HCHK_STATUS_L7STS, which indicates
332 * that the agent returned "fail", "stopped" or "down".
333 * The implication here is that failure to connect to the agent
334 * as a secondary check should not cause the server to be marked
335 * down. */
336 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
337 return;
338
Willy Tarreau4eec5472014-05-20 22:32:27 +0200339 if (check->health > 0)
340 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100341
Willy Tarreau4eec5472014-05-20 22:32:27 +0200342 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200343 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200344}
345
Willy Tarreauaf549582014-05-16 17:37:50 +0200346/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200347 * it isn't in maintenance, it is not tracking a down server and other checks
348 * comply. The rule is simple : by default, a server is up, unless any of the
349 * following conditions is true :
350 * - health check failed (check->health < rise)
351 * - agent check failed (agent->health < rise)
352 * - the server tracks a down server (track && track->state == STOPPED)
353 * Note that if the server has a slowstart, it will switch to STARTING instead
354 * of RUNNING. Also, only the health checks support the nolb mode, so the
355 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200356 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200357static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200358{
Simon Horman4a741432013-02-23 15:35:38 +0900359 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100360
Emeric Brun52a91d32017-08-31 14:41:55 +0200361 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200362 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100363
Emeric Brun52a91d32017-08-31 14:41:55 +0200364 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200365 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100366
Willy Tarreau3e048382014-05-21 10:30:54 +0200367 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
368 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100369
Willy Tarreau3e048382014-05-21 10:30:54 +0200370 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
371 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200372
Emeric Brun52a91d32017-08-31 14:41:55 +0200373 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200374 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100375
Emeric Brun5a133512017-10-19 14:42:30 +0200376 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100377}
378
Willy Tarreaudb58b792014-05-21 13:57:23 +0200379/* Marks the check <check> as valid and tries to set its server into stopping mode
380 * if it was running or starting, and provided it isn't in maintenance and other
381 * checks comply. The conditions for the server to be marked in stopping mode are
382 * the same as for it to be turned up. Also, only the health checks support the
383 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200384 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200385static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200386{
Simon Horman4a741432013-02-23 15:35:38 +0900387 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100388
Emeric Brun52a91d32017-08-31 14:41:55 +0200389 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200390 return;
391
Willy Tarreaudb58b792014-05-21 13:57:23 +0200392 if (check->state & CHK_ST_AGENT)
393 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100394
Emeric Brun52a91d32017-08-31 14:41:55 +0200395 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200396 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100397
Willy Tarreaudb58b792014-05-21 13:57:23 +0200398 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
399 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100400
Willy Tarreaudb58b792014-05-21 13:57:23 +0200401 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
402 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100403
Willy Tarreaub26881a2017-12-23 11:16:49 +0100404 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100405}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200406
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100407/* note: use health_adjust() only, which first checks that the observe mode is
408 * enabled.
409 */
410void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100411{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100412 int failed;
413 int expire;
414
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100415 if (s->observe >= HANA_OBS_SIZE)
416 return;
417
Willy Tarreaubb956662013-01-24 00:37:39 +0100418 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100419 return;
420
421 switch (analyze_statuses[status].lr[s->observe - 1]) {
422 case 1:
423 failed = 1;
424 break;
425
426 case 2:
427 failed = 0;
428 break;
429
430 default:
431 return;
432 }
433
434 if (!failed) {
435 /* good: clear consecutive_errors */
436 s->consecutive_errors = 0;
437 return;
438 }
439
Olivier Houchard7059c552019-03-08 18:49:32 +0100440 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100441
442 if (s->consecutive_errors < s->consecutive_errors_limit)
443 return;
444
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100445 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
446 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100447
448 switch (s->onerror) {
449 case HANA_ONERR_FASTINTER:
450 /* force fastinter - nothing to do here as all modes force it */
451 break;
452
453 case HANA_ONERR_SUDDTH:
454 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900455 if (s->check.health > s->check.rise)
456 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100457
458 /* no break - fall through */
459
460 case HANA_ONERR_FAILCHK:
461 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200462 set_server_check_status(&s->check, HCHK_STATUS_HANA,
463 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200464 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100465 break;
466
467 case HANA_ONERR_MARKDWN:
468 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900469 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200470 set_server_check_status(&s->check, HCHK_STATUS_HANA,
471 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200472 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100473 break;
474
475 default:
476 /* write a warning? */
477 break;
478 }
479
480 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100481 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100482
Simon Horman66183002013-02-23 10:16:43 +0900483 if (s->check.fastinter) {
484 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300485 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200486 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300487 /* requeue check task with new expire */
488 task_queue(s->check.task);
489 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100490 }
Willy Tarreauef781042010-01-27 11:53:01 +0100491}
492
Christopher Faulet61cc8522020-04-20 14:54:42 +0200493/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100494 * closed, keep errno intact as it is supposed to contain the valid error code.
495 * If no error is reported, check the socket's error queue using getsockopt().
496 * Warning, this must be done only once when returning from poll, and never
497 * after an I/O error was attempted, otherwise the error queue might contain
498 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
499 * socket. Returns non-zero if an error was reported, zero if everything is
500 * clean (including a properly closed socket).
501 */
502static int retrieve_errno_from_socket(struct connection *conn)
503{
504 int skerr;
505 socklen_t lskerr = sizeof(skerr);
506
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100507 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100508 return 1;
509
Willy Tarreau3c728722014-01-23 13:50:42 +0100510 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100511 return 0;
512
Willy Tarreau585744b2017-08-24 14:31:19 +0200513 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100514 errno = skerr;
515
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100516 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100517
518 if (!errno) {
519 /* we could not retrieve an error, that does not mean there is
520 * none. Just don't change anything and only report the prior
521 * error if any.
522 */
523 if (conn->flags & CO_FL_ERROR)
524 return 1;
525 else
526 return 0;
527 }
528
529 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
530 return 1;
531}
532
Christopher Faulet61cc8522020-04-20 14:54:42 +0200533/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100534 * and adjust the server status accordingly. It may make use of <errno_bck>
535 * if non-null when the caller is absolutely certain of its validity (eg:
536 * checked just after a syscall). If the caller doesn't have a valid errno,
537 * it can pass zero, and retrieve_errno_from_socket() will be called to try
538 * to extract errno from the socket. If no error is reported, it will consider
539 * the <expired> flag. This is intended to be used when a connection error was
540 * reported in conn->flags or when a timeout was reported in <expired>. The
541 * function takes care of not updating a server status which was already set.
542 * All situations where at least one of <expired> or CO_FL_ERROR are set
543 * produce a status.
544 */
Willy Tarreaub5259bf2017-10-04 14:47:29 +0200545static void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100546{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200547 struct conn_stream *cs = check->cs;
548 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100549 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200550 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200551 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100552
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100553 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554 return;
555
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100556 errno = unclean_errno(errno_bck);
557 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100558 retrieve_errno_from_socket(conn);
559
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200560 if (conn && !(conn->flags & CO_FL_ERROR) &&
561 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100562 return;
563
564 /* we'll try to build a meaningful error message depending on the
565 * context of the error possibly present in conn->err_code, and the
566 * socket error possibly collected above. This is useful to know the
567 * exact step of the L6 layer (eg: SSL handshake).
568 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200569 chk = get_trash_chunk();
570
Christopher Faulet799f3a42020-04-07 12:06:14 +0200571 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200572 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200573 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200574 if (!step)
575 chunk_printf(chk, " at initial connection step of tcp-check");
576 else {
577 chunk_printf(chk, " at step %d of tcp-check", step);
578 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200579 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
580 if (check->current_step->connect.port)
581 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200582 else
583 chunk_appendf(chk, " (connect)");
584 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200585 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
586 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100587
588 switch (expect->type) {
589 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200590 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100591 break;
592 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200593 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100594 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200595 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200596 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100597 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200598 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100599 chunk_appendf(chk, " (expect binary regex)");
600 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200601 case TCPCHK_EXPECT_STRING_LF:
602 chunk_appendf(chk, " (expect log-format string)");
603 break;
604 case TCPCHK_EXPECT_BINARY_LF:
605 chunk_appendf(chk, " (expect log-format binary)");
606 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200607 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200608 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200609 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200610 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200611 chunk_appendf(chk, " (expect HTTP status regex)");
612 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200613 case TCPCHK_EXPECT_HTTP_HEADER:
614 chunk_appendf(chk, " (expect HTTP header pattern)");
615 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200616 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200617 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200618 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200619 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200620 chunk_appendf(chk, " (expect HTTP body regex)");
621 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200622 case TCPCHK_EXPECT_HTTP_BODY_LF:
623 chunk_appendf(chk, " (expect log-format HTTP body)");
624 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200625 case TCPCHK_EXPECT_CUSTOM:
626 chunk_appendf(chk, " (expect custom function)");
627 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100628 case TCPCHK_EXPECT_UNDEF:
629 chunk_appendf(chk, " (undefined expect!)");
630 break;
631 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200632 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200633 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200634 chunk_appendf(chk, " (send)");
635 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200636
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200637 if (check->current_step && check->current_step->comment)
638 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200639 }
640 }
641
Willy Tarreau00149122017-10-04 18:05:01 +0200642 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100643 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200644 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
645 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100646 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200647 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
648 chk->area);
649 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100650 }
651 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100652 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200653 chunk_printf(&trash, "%s%s", strerror(errno),
654 chk->area);
655 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100656 }
657 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200658 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100659 }
660 }
661
Willy Tarreau00149122017-10-04 18:05:01 +0200662 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200663 /* NOTE: this is reported after <fall> tries */
664 chunk_printf(chk, "No port available for the TCP connection");
665 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
666 }
667
Willy Tarreau00149122017-10-04 18:05:01 +0200668 if (!conn) {
669 /* connection allocation error before the connection was established */
670 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
671 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100672 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100673 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200674 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100675 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
676 else if (expired)
677 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200678
679 /*
680 * might be due to a server IP change.
681 * Let's trigger a DNS resolution if none are currently running.
682 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100683 if (check->server)
684 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200685
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100686 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100687 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100688 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200689 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100690 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
691 else if (expired)
692 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
693 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200694 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100695 /* I/O error after connection was established and before we could diagnose */
696 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
697 }
698 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200699 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
700
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100701 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200702 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
703 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200704 tout = check->current_step->expect.tout_status;
705 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100706 }
707
708 return;
709}
710
Willy Tarreaubaaee002006-06-26 02:48:02 +0200711
Christopher Faulet61cc8522020-04-20 14:54:42 +0200712/**************************************************************************/
713/*************** Init/deinit tcp-check rules and ruleset ******************/
714/**************************************************************************/
715/* Releases memory allocated for a log-format string */
716static void free_tcpcheck_fmt(struct list *fmt)
Willy Tarreau20bea422012-07-06 12:00:49 +0200717{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200718 struct logformat_node *lf, *lfb;
Willy Tarreau2d351b62013-12-05 02:36:25 +0100719
Christopher Faulet61cc8522020-04-20 14:54:42 +0200720 list_for_each_entry_safe(lf, lfb, fmt, list) {
721 LIST_DEL(&lf->list);
722 release_sample_expr(lf->expr);
723 free(lf->arg);
724 free(lf);
Willy Tarreau2d351b62013-12-05 02:36:25 +0100725 }
Willy Tarreau20bea422012-07-06 12:00:49 +0200726}
727
Christopher Faulet61cc8522020-04-20 14:54:42 +0200728/* Releases memory allocated for an HTTP header used in a tcp-check send rule */
729static void free_tcpcheck_http_hdr(struct tcpcheck_http_hdr *hdr)
Willy Tarreau2e993902011-10-31 11:53:20 +0100730{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200731 if (!hdr)
732 return;
Willy Tarreau4fc49a92019-05-05 06:54:22 +0200733
Christopher Faulet61cc8522020-04-20 14:54:42 +0200734 free_tcpcheck_fmt(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200735 istfree(&hdr->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200736 free(hdr);
Willy Tarreau2e993902011-10-31 11:53:20 +0100737}
738
Christopher Faulet61cc8522020-04-20 14:54:42 +0200739/* Releases memory allocated for an HTTP header list used in a tcp-check send
740 * rule
Willy Tarreau894c6422017-10-04 15:58:52 +0200741 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200742static void free_tcpcheck_http_hdrs(struct list *hdrs)
Willy Tarreau894c6422017-10-04 15:58:52 +0200743{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200744 struct tcpcheck_http_hdr *hdr, *bhdr;
Willy Tarreau894c6422017-10-04 15:58:52 +0200745
Christopher Faulet61cc8522020-04-20 14:54:42 +0200746 list_for_each_entry_safe(hdr, bhdr, hdrs, list) {
747 LIST_DEL(&hdr->list);
748 free_tcpcheck_http_hdr(hdr);
Willy Tarreau894c6422017-10-04 15:58:52 +0200749 }
Willy Tarreau894c6422017-10-04 15:58:52 +0200750}
751
Christopher Faulet61cc8522020-04-20 14:54:42 +0200752/* Releases memory allocated for a tcp-check. If in_pool is set, it means the
753 * tcp-check was allocated using a memory pool (it is used to instantiate email
754 * alerts).
Christopher Faulet95226db2020-04-15 11:34:04 +0200755 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200756static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
Christopher Faulet95226db2020-04-15 11:34:04 +0200757{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200758 if (!rule)
759 return;
Christopher Faulet95226db2020-04-15 11:34:04 +0200760
Christopher Faulet61cc8522020-04-20 14:54:42 +0200761 free(rule->comment);
762 switch (rule->action) {
763 case TCPCHK_ACT_SEND:
764 switch (rule->send.type) {
765 case TCPCHK_SEND_STRING:
766 case TCPCHK_SEND_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200767 istfree(&rule->send.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200768 break;
769 case TCPCHK_SEND_STRING_LF:
770 case TCPCHK_SEND_BINARY_LF:
771 free_tcpcheck_fmt(&rule->send.fmt);
772 break;
773 case TCPCHK_SEND_HTTP:
774 free(rule->send.http.meth.str.area);
775 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200776 istfree(&rule->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200777 else
778 free_tcpcheck_fmt(&rule->send.http.uri_fmt);
Christopher Fauletb61caf42020-04-21 10:57:42 +0200779 istfree(&rule->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200780 free_tcpcheck_http_hdrs(&rule->send.http.hdrs);
781 if (!(rule->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +0200782 istfree(&rule->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200783 else
784 free_tcpcheck_fmt(&rule->send.http.body_fmt);
785 break;
786 case TCPCHK_SEND_UNDEF:
787 break;
788 }
789 break;
790 case TCPCHK_ACT_EXPECT:
791 free_tcpcheck_fmt(&rule->expect.onerror_fmt);
792 free_tcpcheck_fmt(&rule->expect.onsuccess_fmt);
793 release_sample_expr(rule->expect.status_expr);
794 switch (rule->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200795 case TCPCHK_EXPECT_HTTP_STATUS:
796 free(rule->expect.codes.codes);
797 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200798 case TCPCHK_EXPECT_STRING:
799 case TCPCHK_EXPECT_BINARY:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200800 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200801 istfree(&rule->expect.data);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200802 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200803 case TCPCHK_EXPECT_STRING_REGEX:
804 case TCPCHK_EXPECT_BINARY_REGEX:
805 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
806 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +0200807 regex_free(rule->expect.regex);
808 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200809 case TCPCHK_EXPECT_STRING_LF:
810 case TCPCHK_EXPECT_BINARY_LF:
811 case TCPCHK_EXPECT_HTTP_BODY_LF:
812 free_tcpcheck_fmt(&rule->expect.fmt);
813 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200814 case TCPCHK_EXPECT_HTTP_HEADER:
815 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
816 regex_free(rule->expect.hdr.name_re);
817 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
818 free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
819 else
820 istfree(&rule->expect.hdr.name);
821
822 if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
823 regex_free(rule->expect.hdr.value_re);
824 else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
825 free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
826 else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
827 istfree(&rule->expect.hdr.value);
828 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200829 case TCPCHK_EXPECT_CUSTOM:
830 case TCPCHK_EXPECT_UNDEF:
831 break;
832 }
833 break;
834 case TCPCHK_ACT_CONNECT:
835 free(rule->connect.sni);
836 free(rule->connect.alpn);
837 release_sample_expr(rule->connect.port_expr);
838 break;
839 case TCPCHK_ACT_COMMENT:
840 break;
841 case TCPCHK_ACT_ACTION_KW:
842 free(rule->action_kw.rule);
843 break;
Christopher Faulet95226db2020-04-15 11:34:04 +0200844 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200845
846 if (in_pool)
847 pool_free(pool_head_tcpcheck_rule, rule);
848 else
849 free(rule);
Christopher Faulet95226db2020-04-15 11:34:04 +0200850}
851
Christopher Faulet61cc8522020-04-20 14:54:42 +0200852/* Creates a tcp-check variable used in preset variables before executing a
853 * tcp-check ruleset.
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100854 */
Christopher Fauletb61caf42020-04-21 10:57:42 +0200855static struct tcpcheck_var *create_tcpcheck_var(const struct ist name)
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100856{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200857 struct tcpcheck_var *var = NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100858
Christopher Faulet61cc8522020-04-20 14:54:42 +0200859 var = calloc(1, sizeof(*var));
860 if (var == NULL)
861 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100862
Christopher Fauletb61caf42020-04-21 10:57:42 +0200863 var->name = istdup(name);
864 if (!isttest(var->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200865 free(var);
866 return NULL;
Gaetan Rivet1d22d7e2020-02-14 17:47:08 +0100867 }
Simon Horman98637e52014-06-20 12:30:16 +0900868
Christopher Faulet61cc8522020-04-20 14:54:42 +0200869 LIST_INIT(&var->list);
870 return var;
Simon Horman98637e52014-06-20 12:30:16 +0900871}
872
Christopher Faulet61cc8522020-04-20 14:54:42 +0200873/* Releases memory allocated for a preset tcp-check variable */
874static void free_tcpcheck_var(struct tcpcheck_var *var)
Simon Horman98637e52014-06-20 12:30:16 +0900875{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200876 if (!var)
877 return;
878
Christopher Fauletb61caf42020-04-21 10:57:42 +0200879 istfree(&var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200880 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN)
881 free(var->data.u.str.area);
882 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER)
883 free(var->data.u.meth.str.area);
884 free(var);
Simon Horman98637e52014-06-20 12:30:16 +0900885}
886
Christopher Faulet61cc8522020-04-20 14:54:42 +0200887/* Releases a list of preset tcp-check variables */
888static void free_tcpcheck_vars(struct list *vars)
Simon Horman98637e52014-06-20 12:30:16 +0900889{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200890 struct tcpcheck_var *var, *back;
Christopher Fauletcfda8472017-10-20 15:40:23 +0200891
Christopher Faulet61cc8522020-04-20 14:54:42 +0200892 list_for_each_entry_safe(var, back, vars, list) {
893 LIST_DEL(&var->list);
894 free_tcpcheck_var(var);
895 }
Simon Horman98637e52014-06-20 12:30:16 +0900896}
897
Christopher Faulet61cc8522020-04-20 14:54:42 +0200898/* Duplicate a list of preset tcp-check variables */
899int dup_tcpcheck_vars(struct list *dst, struct list *src)
Simon Horman98637e52014-06-20 12:30:16 +0900900{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200901 struct tcpcheck_var *var, *new = NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900902
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903 list_for_each_entry(var, src, list) {
Christopher Fauletb61caf42020-04-21 10:57:42 +0200904 new = create_tcpcheck_var(var->name);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200905 if (!new)
906 goto error;
907 new->data.type = var->data.type;
908 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
909 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
910 goto error;
911 if (var->data.type == SMP_T_STR)
912 new->data.u.str.area[new->data.u.str.data] = 0;
913 }
914 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
915 if (chunk_dup(&new->data.u.str, &var->data.u.str) == NULL)
916 goto error;
917 new->data.u.str.area[new->data.u.str.data] = 0;
918 new->data.u.meth.meth = var->data.u.meth.meth;
919 }
920 else
921 new->data.u = var->data.u;
922 LIST_ADDQ(dst, &new->list);
923 }
924 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900925
Christopher Faulet61cc8522020-04-20 14:54:42 +0200926 error:
927 free(new);
928 return 0;
929}
Christopher Fauletcfda8472017-10-20 15:40:23 +0200930
Christopher Faulet61cc8522020-04-20 14:54:42 +0200931/* Looks for a shared tcp-check ruleset given its name. */
932static struct tcpcheck_ruleset *find_tcpcheck_ruleset(const char *name)
933{
934 struct tcpcheck_ruleset *rs;
Christopher Fauletd7cee712020-04-21 13:45:00 +0200935 struct ebpt_node *node;
Simon Horman98637e52014-06-20 12:30:16 +0900936
Christopher Fauletd7cee712020-04-21 13:45:00 +0200937 node = ebis_lookup_len(&shared_tcpchecks, name, strlen(name));
938 if (node) {
939 rs = container_of(node, typeof(*rs), node);
940 return rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200941 }
942 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900943}
944
Christopher Faulet56192cc2020-05-29 08:10:50 +0200945/* Creates a new shared tcp-check ruleset and insert it in shared_tcpchecks
946 * tree.
947 */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200948static struct tcpcheck_ruleset *create_tcpcheck_ruleset(const char *name)
Simon Horman98637e52014-06-20 12:30:16 +0900949{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200950 struct tcpcheck_ruleset *rs;
Simon Horman98637e52014-06-20 12:30:16 +0900951
Christopher Faulet61cc8522020-04-20 14:54:42 +0200952 rs = calloc(1, sizeof(*rs));
953 if (rs == NULL)
954 return NULL;
955
Christopher Fauletd7cee712020-04-21 13:45:00 +0200956 rs->node.key = strdup(name);
957 if (rs->node.key == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +0200958 free(rs);
959 return NULL;
Simon Horman98637e52014-06-20 12:30:16 +0900960 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200961
Christopher Faulet61cc8522020-04-20 14:54:42 +0200962 LIST_INIT(&rs->rules);
Christopher Fauletd7cee712020-04-21 13:45:00 +0200963 ebis_insert(&shared_tcpchecks, &rs->node);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 return rs;
Simon Horman98637e52014-06-20 12:30:16 +0900965}
966
Christopher Faulet61cc8522020-04-20 14:54:42 +0200967/* Releases memory allocated by a tcp-check ruleset. */
968static void free_tcpcheck_ruleset(struct tcpcheck_ruleset *rs)
Simon Horman98637e52014-06-20 12:30:16 +0900969{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200970 struct tcpcheck_rule *r, *rb;
971 if (!rs)
972 return;
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200973
Christopher Fauletd7cee712020-04-21 13:45:00 +0200974 ebpt_delete(&rs->node);
975 free(rs->node.key);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200976 list_for_each_entry_safe(r, rb, &rs->rules, list) {
977 LIST_DEL(&r->list);
978 free_tcpcheck(r, 0);
979 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200980 free(rs);
Simon Horman98637e52014-06-20 12:30:16 +0900981}
982
Christopher Faulet61cc8522020-04-20 14:54:42 +0200983
984/**************************************************************************/
985/**************** Everything about tcp-checks execution *******************/
986/**************************************************************************/
987/* Returns the id of a step in a tcp-check ruleset */
988static int tcpcheck_get_step_id(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau48d6bf22016-06-21 16:27:34 +0200989{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200990 if (!rule)
991 rule = check->current_step;
Simon Horman98637e52014-06-20 12:30:16 +0900992
Christopher Faulet61cc8522020-04-20 14:54:42 +0200993 /* no last started step => first step */
994 if (!rule)
Simon Horman98637e52014-06-20 12:30:16 +0900995 return 1;
Simon Horman98637e52014-06-20 12:30:16 +0900996
Christopher Faulet61cc8522020-04-20 14:54:42 +0200997 /* last step is the first implicit connect */
998 if (rule->index == 0 &&
999 rule->action == TCPCHK_ACT_CONNECT &&
1000 (rule->connect.options & TCPCHK_OPT_IMPLICIT))
1001 return 0;
Simon Horman98637e52014-06-20 12:30:16 +09001002
Christopher Faulet61cc8522020-04-20 14:54:42 +02001003 return rule->index + 1;
Simon Horman98637e52014-06-20 12:30:16 +09001004}
1005
Christopher Faulet61cc8522020-04-20 14:54:42 +02001006/* Returns the first non COMMENT/ACTION_KW tcp-check rule from list <list> or
1007 * NULL if none was found.
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001008 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001010{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001011 struct tcpcheck_rule *r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001012
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 list_for_each_entry(r, rules->list, list) {
1014 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1015 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001016 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001017 return NULL;
1018}
Cyril Bontéac92a062014-12-27 22:28:38 +01001019
Christopher Faulet61cc8522020-04-20 14:54:42 +02001020/* Returns the last non COMMENT/ACTION_KW tcp-check rule from list <list> or
1021 * NULL if none was found.
1022 */
1023static struct tcpcheck_rule *get_last_tcpcheck_rule(struct tcpcheck_rules *rules)
1024{
1025 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001026
Christopher Faulet61cc8522020-04-20 14:54:42 +02001027 list_for_each_entry_rev(r, rules->list, list) {
1028 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1029 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001030 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001031 return NULL;
1032}
Cyril Bontéac92a062014-12-27 22:28:38 +01001033
Christopher Faulet61cc8522020-04-20 14:54:42 +02001034/* Returns the non COMMENT/ACTION_KW tcp-check rule from list <list> following
1035 * <start> or NULL if non was found. If <start> is NULL, it relies on
1036 * get_first_tcpcheck_rule().
1037 */
1038static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
1039{
1040 struct tcpcheck_rule *r;
Cyril Bontéac92a062014-12-27 22:28:38 +01001041
Christopher Faulet61cc8522020-04-20 14:54:42 +02001042 if (!start)
1043 return get_first_tcpcheck_rule(rules);
Cyril Bontéac92a062014-12-27 22:28:38 +01001044
Christopher Faulet61cc8522020-04-20 14:54:42 +02001045 r = LIST_NEXT(&start->list, typeof(r), list);
1046 list_for_each_entry_from(r, rules->list, list) {
1047 if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
1048 return r;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001049 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001050 return NULL;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001051}
Simon Horman98637e52014-06-20 12:30:16 +09001052
Simon Horman98637e52014-06-20 12:30:16 +09001053
Christopher Faulet61cc8522020-04-20 14:54:42 +02001054/* Creates info message when a tcp-check healthcheck fails on an expect rule */
1055static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1056 int match, struct ist info)
1057{
1058 struct sample *smp;
Simon Horman98637e52014-06-20 12:30:16 +09001059
Christopher Faulet61cc8522020-04-20 14:54:42 +02001060 /* Follows these step to produce the info message:
1061 * 1. if info field is already provided, copy it
1062 * 2. if the expect rule provides an onerror log-format string,
1063 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001064 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001065 * 4. Otherwise produce the generic tcp-check info message
1066 */
1067 if (istlen(info)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02001068 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001069 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001070 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001071 else if (!LIST_ISEMPTY(&rule->expect.onerror_fmt)) {
1072 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), &rule->expect.onerror_fmt);
1073 goto comment;
Cyril Bontéac92a062014-12-27 22:28:38 +01001074 }
Simon Horman98637e52014-06-20 12:30:16 +09001075
Christopher Faulet61cc8522020-04-20 14:54:42 +02001076 if (check->type == PR_O2_TCPCHK_CHK &&
1077 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_TCP_CHK)
1078 goto comment;
Simon Horman98637e52014-06-20 12:30:16 +09001079
Christopher Faulet61cc8522020-04-20 14:54:42 +02001080 chunk_strcat(msg, (match ? "TCPCHK matched unwanted content" : "TCPCHK did not match content"));
1081 switch (rule->expect.type) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001082 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02001083 chunk_appendf(msg, "(status codes) at step %d", tcpcheck_get_step_id(check, rule));
1084 break;
1085 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001086 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02001087 chunk_appendf(msg, " '%.*s' at step %d", (unsigned int)istlen(rule->expect.data), istptr(rule->expect.data),
Christopher Faulet61cc8522020-04-20 14:54:42 +02001088 tcpcheck_get_step_id(check, rule));
1089 break;
1090 case TCPCHK_EXPECT_BINARY:
1091 chunk_appendf(msg, " (binary) at step %d", tcpcheck_get_step_id(check, rule));
1092 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001093 case TCPCHK_EXPECT_STRING_REGEX:
1094 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
1095 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001096 chunk_appendf(msg, " (regex) at step %d", tcpcheck_get_step_id(check, rule));
1097 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02001098 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001099 chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001100 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001101 case TCPCHK_EXPECT_STRING_LF:
1102 case TCPCHK_EXPECT_HTTP_BODY_LF:
1103 chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
1104 break;
1105 case TCPCHK_EXPECT_BINARY_LF:
1106 chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
1107 break;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001108 case TCPCHK_EXPECT_CUSTOM:
1109 chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
1110 break;
Christopher Faulet39708192020-05-05 10:47:36 +02001111 case TCPCHK_EXPECT_HTTP_HEADER:
1112 chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001113 case TCPCHK_EXPECT_UNDEF:
1114 /* Should never happen. */
1115 return;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001116 }
1117
Christopher Faulet61cc8522020-04-20 14:54:42 +02001118 comment:
1119 /* If the failing expect rule provides a comment, it is concatenated to
1120 * the info message.
1121 */
1122 if (rule->comment) {
1123 chunk_strcat(msg, " comment: ");
Christopher Faulet88d939c2020-04-22 15:32:11 +02001124 chunk_strcat(msg, rule->comment);
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001125 }
Willy Tarreau04276f32017-01-06 17:41:29 +01001126
Christopher Faulet61cc8522020-04-20 14:54:42 +02001127 /* Finally, the check status code is set if the failing expect rule
1128 * defines a status expression.
1129 */
1130 if (rule->expect.status_expr) {
1131 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001132 rule->expect.status_expr, SMP_T_STR);
1133
1134 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1135 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 check->code = smp->data.u.sint;
Cyril Bontéac92a062014-12-27 22:28:38 +01001137 }
Simon Horman98637e52014-06-20 12:30:16 +09001138
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 *(b_tail(msg)) = '\0';
1140}
Cyril Bontéac92a062014-12-27 22:28:38 +01001141
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142/* Creates info message when a tcp-check healthcheck succeeds on an expect rule */
1143static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *check, struct tcpcheck_rule *rule,
1144 struct ist info)
1145{
1146 struct sample *smp;
Cyril Bonté9ede66b2014-12-02 21:21:36 +01001147
Christopher Faulet61cc8522020-04-20 14:54:42 +02001148 /* Follows these step to produce the info message:
1149 * 1. if info field is already provided, copy it
1150 * 2. if the expect rule provides an onsucces log-format string,
1151 * use it to produce the message
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001152 * 3. the expect rule is part of a protocol check (http, redis, mysql...), do nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02001153 * 4. Otherwise produce the generic tcp-check info message
1154 */
1155 if (istlen(info))
Christopher Fauletb61caf42020-04-21 10:57:42 +02001156 chunk_strncat(msg, istptr(info), istlen(info));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001157 if (!LIST_ISEMPTY(&rule->expect.onsuccess_fmt))
1158 msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
1159 &rule->expect.onsuccess_fmt);
1160 else if (check->type == PR_O2_TCPCHK_CHK &&
1161 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK)
1162 chunk_strcat(msg, "(tcp-check)");
Simon Horman98637e52014-06-20 12:30:16 +09001163
Christopher Faulet61cc8522020-04-20 14:54:42 +02001164 /* Finally, the check status code is set if the expect rule defines a
1165 * status expression.
1166 */
1167 if (rule->expect.status_expr) {
1168 smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
Christopher Faulet78f371e2020-04-30 09:38:08 +02001169 rule->expect.status_expr, SMP_T_STR);
1170
1171 if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
1172 sample_casts[smp->data.type][SMP_T_SINT](smp))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001173 check->code = smp->data.u.sint;
Simon Horman98637e52014-06-20 12:30:16 +09001174 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001175
1176 *(b_tail(msg)) = '\0';
Simon Horman98637e52014-06-20 12:30:16 +09001177}
1178
Christopher Faulet61cc8522020-04-20 14:54:42 +02001179/* Builds the server state header used by HTTP health-checks */
1180static int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +09001181{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001182 int sv_state;
1183 int ratio;
1184 char addr[46];
1185 char port[6];
1186 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
1187 "UP %d/%d", "UP",
1188 "NOLB %d/%d", "NOLB",
1189 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +09001190
Christopher Faulet61cc8522020-04-20 14:54:42 +02001191 if (!(s->check.state & CHK_ST_ENABLED))
1192 sv_state = 6;
1193 else if (s->cur_state != SRV_ST_STOPPED) {
1194 if (s->check.health == s->check.rise + s->check.fall - 1)
1195 sv_state = 3; /* UP */
1196 else
1197 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +09001198
Christopher Faulet61cc8522020-04-20 14:54:42 +02001199 if (s->cur_state == SRV_ST_STOPPING)
1200 sv_state += 2;
1201 } else {
1202 if (s->check.health)
1203 sv_state = 1; /* going up */
1204 else
1205 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +09001206 }
Willy Tarreaub7b24782016-06-21 15:32:29 +02001207
Christopher Faulet61cc8522020-04-20 14:54:42 +02001208 chunk_appendf(buf, srv_hlt_st[sv_state],
1209 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
1210 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +02001211
Christopher Faulet61cc8522020-04-20 14:54:42 +02001212 addr_to_str(&s->addr, addr, sizeof(addr));
1213 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1214 snprintf(port, sizeof(port), "%u", s->svc_port);
1215 else
1216 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +02001217
Christopher Faulet61cc8522020-04-20 14:54:42 +02001218 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
1219 addr, port, s->proxy->id, s->id,
1220 global.node,
1221 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1222 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
1223 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
1224 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +01001225
Christopher Faulet61cc8522020-04-20 14:54:42 +02001226 if ((s->cur_state == SRV_ST_STARTING) &&
1227 now.tv_sec < s->last_change + s->slowstart &&
1228 now.tv_sec >= s->last_change) {
1229 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
1230 chunk_appendf(buf, "; throttle=%d%%", ratio);
1231 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 return b_data(buf);
1234}
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001235
Christopher Faulet61cc8522020-04-20 14:54:42 +02001236/* Internal functions to parse and validate a MySQL packet in the context of an
1237 * expect rule. It start to parse the input buffer at the offset <offset>. If
1238 * <last_read> is set, no more data are expected.
1239 */
1240static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
1241 unsigned int offset, int last_read)
1242{
1243 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1244 enum healthcheck_status status;
1245 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001246 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001247 unsigned int err = 0, plen = 0;
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001248
Christopher Fauletaaae9a02020-04-26 09:50:31 +02001249
Christopher Faulet61cc8522020-04-20 14:54:42 +02001250 /* 3 Bytes for the packet length and 1 byte for the sequence id */
Christopher Faulet733dd732020-04-28 10:24:23 +02001251 if (b_data(&check->bi) < offset+4) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001252 if (!last_read)
1253 goto wait_more_data;
1254
1255 /* invalid length or truncated response */
1256 status = HCHK_STATUS_L7RSP;
1257 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001258 }
1259
Christopher Faulet61cc8522020-04-20 14:54:42 +02001260 plen = ((unsigned char) *b_peek(&check->bi, offset)) +
1261 (((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
1262 (((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001263
Christopher Faulet61cc8522020-04-20 14:54:42 +02001264 if (b_data(&check->bi) < offset+plen+4) {
1265 if (!last_read)
1266 goto wait_more_data;
1267
1268 /* invalid length or truncated response */
1269 status = HCHK_STATUS_L7RSP;
1270 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001271 }
Simon Horman98637e52014-06-20 12:30:16 +09001272
Christopher Faulet61cc8522020-04-20 14:54:42 +02001273 if (*b_peek(&check->bi, offset+4) == '\xff') {
1274 /* MySQL Error packet always begin with field_count = 0xff */
1275 status = HCHK_STATUS_L7STS;
1276 err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
1277 (((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
1278 desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
1279 goto error;
1280 }
Simon Horman98637e52014-06-20 12:30:16 +09001281
Christopher Faulet61cc8522020-04-20 14:54:42 +02001282 if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
1283 /* Not the last rule, continue */
1284 goto out;
1285 }
Simon Horman98637e52014-06-20 12:30:16 +09001286
Christopher Faulet61cc8522020-04-20 14:54:42 +02001287 /* We set the MySQL Version in description for information purpose
1288 * FIXME : it can be cool to use MySQL Version for other purpose,
1289 * like mark as down old MySQL server.
1290 */
Christopher Faulet1941bab2020-05-05 07:55:50 +02001291 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1292 set_server_check_status(check, status, b_peek(&check->bi, 5));
Simon Horman98637e52014-06-20 12:30:16 +09001293
Christopher Faulet61cc8522020-04-20 14:54:42 +02001294 out:
1295 free_trash_chunk(msg);
1296 return ret;
Simon Horman98637e52014-06-20 12:30:16 +09001297
Christopher Faulet61cc8522020-04-20 14:54:42 +02001298 error:
1299 ret = TCPCHK_EVAL_STOP;
1300 check->code = err;
1301 msg = alloc_trash_chunk();
1302 if (msg)
1303 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1304 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1305 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001306
Christopher Faulet61cc8522020-04-20 14:54:42 +02001307 wait_more_data:
1308 ret = TCPCHK_EVAL_WAIT;
1309 goto out;
1310}
Simon Horman98637e52014-06-20 12:30:16 +09001311
Christopher Faulet61cc8522020-04-20 14:54:42 +02001312/* Custom tcp-check expect function to parse and validate the MySQL initial
1313 * handshake packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1314 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1315 * error occurred.
1316 */
1317static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
1318{
1319 return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
1320}
Simon Horman98637e52014-06-20 12:30:16 +09001321
Christopher Faulet61cc8522020-04-20 14:54:42 +02001322/* Custom tcp-check expect function to parse and validate the MySQL OK packet
1323 * following the initial handshake. Returns TCPCHK_EVAL_WAIT to wait for more
1324 * data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if
1325 * an error occurred.
1326 */
1327static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
1328{
1329 unsigned int hslen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001330
Christopher Faulet61cc8522020-04-20 14:54:42 +02001331 hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
1332 (((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
1333 (((unsigned char) *(b_peek(&check->bi, 2))) << 16);
Simon Horman98637e52014-06-20 12:30:16 +09001334
Christopher Faulet61cc8522020-04-20 14:54:42 +02001335 return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
1336}
Simon Horman98637e52014-06-20 12:30:16 +09001337
Christopher Faulet61cc8522020-04-20 14:54:42 +02001338/* Custom tcp-check expect function to parse and validate the LDAP bind response
1339 * package packet. Returns TCPCHK_EVAL_WAIT to wait for more data,
1340 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
1341 * error occurred.
1342 */
1343static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
1344{
1345 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1346 enum healthcheck_status status;
1347 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001348 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001349 unsigned short msglen = 0;
Simon Horman98637e52014-06-20 12:30:16 +09001350
Christopher Faulet61cc8522020-04-20 14:54:42 +02001351 /* Check if the server speaks LDAP (ASN.1/BER)
1352 * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
1353 * http://tools.ietf.org/html/rfc4511
1354 */
1355 /* size of LDAPMessage */
1356 msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 1) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001357
Christopher Faulet61cc8522020-04-20 14:54:42 +02001358 /* http://tools.ietf.org/html/rfc4511#section-4.2.2
1359 * messageID: 0x02 0x01 0x01: INTEGER 1
1360 * protocolOp: 0x61: bindResponse
1361 */
1362 if ((msglen > 2) || (memcmp(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
1363 status = HCHK_STATUS_L7RSP;
1364 desc = ist("Not LDAPv3 protocol");
1365 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001366 }
Simon Horman98637e52014-06-20 12:30:16 +09001367
Christopher Faulet61cc8522020-04-20 14:54:42 +02001368 /* size of bindResponse */
1369 msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
Simon Horman98637e52014-06-20 12:30:16 +09001370
Christopher Faulet61cc8522020-04-20 14:54:42 +02001371 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1372 * ldapResult: 0x0a 0x01: ENUMERATION
1373 */
1374 if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
1375 status = HCHK_STATUS_L7RSP;
1376 desc = ist("Not LDAPv3 protocol");
1377 goto error;
1378 }
Simon Horman98637e52014-06-20 12:30:16 +09001379
Christopher Faulet61cc8522020-04-20 14:54:42 +02001380 /* http://tools.ietf.org/html/rfc4511#section-4.1.9
1381 * resultCode
1382 */
1383 check->code = *(b_head(&check->bi) + msglen + 9);
1384 if (check->code) {
1385 status = HCHK_STATUS_L7STS;
1386 desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
1387 goto error;
Simon Horman98637e52014-06-20 12:30:16 +09001388 }
1389
Christopher Faulet1941bab2020-05-05 07:55:50 +02001390 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1391 set_server_check_status(check, status, "Success");
Willy Tarreau62ac84f2017-11-05 10:11:13 +01001392
Christopher Faulet61cc8522020-04-20 14:54:42 +02001393 out:
1394 free_trash_chunk(msg);
1395 return ret;
1396
1397 error:
1398 ret = TCPCHK_EVAL_STOP;
1399 msg = alloc_trash_chunk();
1400 if (msg)
1401 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1402 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1403 goto out;
Simon Horman98637e52014-06-20 12:30:16 +09001404}
1405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406/* Custom tcp-check expect function to parse and validate the SPOP hello agent
1407 * frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1408 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
Simon Horman98637e52014-06-20 12:30:16 +09001409 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410static 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 +02001411{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001412 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1413 enum healthcheck_status status;
1414 struct buffer *msg = NULL;
Christopher Fauletb61caf42020-04-21 10:57:42 +02001415 struct ist desc = IST_NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416 unsigned int framesz;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001417
Willy Tarreaubaaee002006-06-26 02:48:02 +02001418
Christopher Faulet61cc8522020-04-20 14:54:42 +02001419 memcpy(&framesz, b_head(&check->bi), 4);
1420 framesz = ntohl(framesz);
Willy Tarreaubaaee002006-06-26 02:48:02 +02001421
Christopher Faulet61cc8522020-04-20 14:54:42 +02001422 if (!last_read && b_data(&check->bi) < (4+framesz))
1423 goto wait_more_data;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001424
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425 memset(b_orig(&trash), 0, b_size(&trash));
1426 if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
1427 status = HCHK_STATUS_L7RSP;
1428 desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
1429 goto error;
1430 }
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +02001431
Christopher Faulet1941bab2020-05-05 07:55:50 +02001432 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
1433 set_server_check_status(check, status, "SPOA server is ok");
Willy Tarreaubaaee002006-06-26 02:48:02 +02001434
Christopher Faulet61cc8522020-04-20 14:54:42 +02001435 out:
1436 free_trash_chunk(msg);
1437 return ret;
Christopher Fauleta32a2502020-04-20 09:04:37 +02001438
Christopher Faulet61cc8522020-04-20 14:54:42 +02001439 error:
1440 ret = TCPCHK_EVAL_STOP;
1441 msg = alloc_trash_chunk();
1442 if (msg)
1443 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
1444 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
1445 goto out;
Willy Tarreau6b0a8502012-11-23 08:51:32 +01001446
Christopher Faulet61cc8522020-04-20 14:54:42 +02001447 wait_more_data:
1448 ret = TCPCHK_EVAL_WAIT;
1449 goto out;
1450}
Willy Tarreaubaaee002006-06-26 02:48:02 +02001451
Christopher Faulet61cc8522020-04-20 14:54:42 +02001452/* Custom tcp-check expect function to parse and validate the agent-check
1453 * reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
1454 * to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
1455 */
1456static enum tcpcheck_eval_ret tcpcheck_agent_expect_reply(struct check *check, struct tcpcheck_rule *rule, int last_read)
1457{
1458 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_STOP;
1459 enum healthcheck_status status = HCHK_STATUS_CHECKED;
1460 const char *hs = NULL; /* health status */
1461 const char *as = NULL; /* admin status */
1462 const char *ps = NULL; /* performance status */
1463 const char *cs = NULL; /* maxconn */
1464 const char *err = NULL; /* first error to report */
1465 const char *wrn = NULL; /* first warning to report */
1466 char *cmd, *p;
Krzysztof Piotr Oledzki5259dfe2008-01-21 01:54:06 +01001467
Christopher Faulet61cc8522020-04-20 14:54:42 +02001468 /* We're getting an agent check response. The agent could
1469 * have been disabled in the mean time with a long check
1470 * still pending. It is important that we ignore the whole
1471 * response.
1472 */
1473 if (!(check->state & CHK_ST_ENABLED))
1474 goto out;
Willy Tarreauf1503172012-09-28 19:39:36 +02001475
Christopher Faulet61cc8522020-04-20 14:54:42 +02001476 /* The agent supports strings made of a single line ended by the
1477 * first CR ('\r') or LF ('\n'). This line is composed of words
1478 * delimited by spaces (' '), tabs ('\t'), or commas (','). The
1479 * line may optionally contained a description of a state change
1480 * after a sharp ('#'), which is only considered if a health state
1481 * is announced.
1482 *
1483 * Words may be composed of :
1484 * - a numeric weight suffixed by the percent character ('%').
1485 * - a health status among "up", "down", "stopped", and "fail".
1486 * - an admin status among "ready", "drain", "maint".
1487 *
1488 * These words may appear in any order. If multiple words of the
1489 * same category appear, the last one wins.
1490 */
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001491
Christopher Faulet61cc8522020-04-20 14:54:42 +02001492 p = b_head(&check->bi);
1493 while (*p && *p != '\n' && *p != '\r')
1494 p++;
Gaetan Rivet05d692d2020-02-14 17:42:54 +01001495
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496 if (!*p) {
1497 if (!last_read)
1498 goto wait_more_data;
1499
1500 /* at least inform the admin that the agent is mis-behaving */
1501 set_server_check_status(check, check->status, "Ignoring incomplete line from agent");
1502 goto out;
1503 }
1504
1505 *p = 0;
1506 cmd = b_head(&check->bi);
1507
1508 while (*cmd) {
1509 /* look for next word */
1510 if (*cmd == ' ' || *cmd == '\t' || *cmd == ',') {
1511 cmd++;
1512 continue;
Willy Tarreau5ba04f62013-02-12 15:23:12 +01001513 }
1514
Christopher Faulet61cc8522020-04-20 14:54:42 +02001515 if (*cmd == '#') {
1516 /* this is the beginning of a health status description,
1517 * skip the sharp and blanks.
Willy Tarreau86eded62019-06-14 14:47:49 +02001518 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001519 cmd++;
1520 while (*cmd == '\t' || *cmd == ' ')
1521 cmd++;
1522 break;
Willy Tarreau00149122017-10-04 18:05:01 +02001523 }
1524
Christopher Faulet61cc8522020-04-20 14:54:42 +02001525 /* find the end of the word so that we have a null-terminated
1526 * word between <cmd> and <p>.
1527 */
1528 p = cmd + 1;
1529 while (*p && *p != '\t' && *p != ' ' && *p != '\n' && *p != ',')
1530 p++;
1531 if (*p)
1532 *p++ = 0;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001533
Christopher Faulet61cc8522020-04-20 14:54:42 +02001534 /* first, health statuses */
1535 if (strcasecmp(cmd, "up") == 0) {
1536 check->server->check.health = check->server->check.rise + check->server->check.fall - 1;
1537 status = HCHK_STATUS_L7OKD;
1538 hs = cmd;
1539 }
1540 else if (strcasecmp(cmd, "down") == 0) {
1541 check->server->check.health = 0;
1542 status = HCHK_STATUS_L7STS;
1543 hs = cmd;
1544 }
1545 else if (strcasecmp(cmd, "stopped") == 0) {
1546 check->server->check.health = 0;
1547 status = HCHK_STATUS_L7STS;
1548 hs = cmd;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001549 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001550 else if (strcasecmp(cmd, "fail") == 0) {
1551 check->server->check.health = 0;
1552 status = HCHK_STATUS_L7STS;
1553 hs = cmd;
1554 }
1555 /* admin statuses */
1556 else if (strcasecmp(cmd, "ready") == 0) {
1557 as = cmd;
1558 }
1559 else if (strcasecmp(cmd, "drain") == 0) {
1560 as = cmd;
1561 }
1562 else if (strcasecmp(cmd, "maint") == 0) {
1563 as = cmd;
1564 }
1565 /* try to parse a weight here and keep the last one */
1566 else if (isdigit((unsigned char)*cmd) && strchr(cmd, '%') != NULL) {
1567 ps = cmd;
1568 }
1569 /* try to parse a maxconn here */
1570 else if (strncasecmp(cmd, "maxconn:", strlen("maxconn:")) == 0) {
1571 cs = cmd;
1572 }
1573 else {
1574 /* keep a copy of the first error */
1575 if (!err)
1576 err = cmd;
1577 }
1578 /* skip to next word */
1579 cmd = p;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001580 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001581 /* here, cmd points either to \0 or to the beginning of a
1582 * description. Skip possible leading spaces.
1583 */
1584 while (*cmd == ' ' || *cmd == '\n')
1585 cmd++;
Willy Tarreaubaaee002006-06-26 02:48:02 +02001586
Christopher Faulet61cc8522020-04-20 14:54:42 +02001587 /* First, update the admin status so that we avoid sending other
1588 * possibly useless warnings and can also update the health if
1589 * present after going back up.
1590 */
1591 if (as) {
1592 if (strcasecmp(as, "drain") == 0)
1593 srv_adm_set_drain(check->server);
1594 else if (strcasecmp(as, "maint") == 0)
1595 srv_adm_set_maint(check->server);
1596 else
1597 srv_adm_set_ready(check->server);
1598 }
Simon Horman98637e52014-06-20 12:30:16 +09001599
Christopher Faulet61cc8522020-04-20 14:54:42 +02001600 /* now change weights */
1601 if (ps) {
1602 const char *msg;
Baptiste Assmanna68ca962015-04-14 01:15:08 +02001603
Christopher Faulet61cc8522020-04-20 14:54:42 +02001604 msg = server_parse_weight_change_request(check->server, ps);
1605 if (!wrn || !*wrn)
1606 wrn = msg;
1607 }
Simon Horman98637e52014-06-20 12:30:16 +09001608
Christopher Faulet61cc8522020-04-20 14:54:42 +02001609 if (cs) {
1610 const char *msg;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001611
Christopher Faulet61cc8522020-04-20 14:54:42 +02001612 cs += strlen("maxconn:");
Willy Tarreau6dd4ac82019-09-03 18:55:02 +02001613
Christopher Faulet61cc8522020-04-20 14:54:42 +02001614 msg = server_parse_maxconn_change_request(check->server, cs);
1615 if (!wrn || !*wrn)
1616 wrn = msg;
Simon Horman5c942422013-11-25 10:46:32 +09001617 }
1618
Christopher Faulet61cc8522020-04-20 14:54:42 +02001619 /* and finally health status */
1620 if (hs) {
1621 /* We'll report some of the warnings and errors we have
1622 * here. Down reports are critical, we leave them untouched.
1623 * Lack of report, or report of 'UP' leaves the room for
1624 * ERR first, then WARN.
1625 */
1626 const char *msg = cmd;
1627 struct buffer *t;
Simon Horman5c942422013-11-25 10:46:32 +09001628
Christopher Faulet61cc8522020-04-20 14:54:42 +02001629 if (!*msg || status == HCHK_STATUS_L7OKD) {
1630 if (err && *err)
1631 msg = err;
1632 else if (wrn && *wrn)
1633 msg = wrn;
1634 }
Willy Tarreau1746eec2014-04-25 10:46:47 +02001635
Christopher Faulet61cc8522020-04-20 14:54:42 +02001636 t = get_trash_chunk();
1637 chunk_printf(t, "via agent : %s%s%s%s",
1638 hs, *msg ? " (" : "",
1639 msg, *msg ? ")" : "");
1640 set_server_check_status(check, status, t->area);
1641 }
1642 else if (err && *err) {
1643 /* No status change but we'd like to report something odd.
1644 * Just report the current state and copy the message.
1645 */
1646 chunk_printf(&trash, "agent reports an error : %s", err);
1647 set_server_check_status(check, status/*check->status*/, trash.area);
1648 }
1649 else if (wrn && *wrn) {
1650 /* No status change but we'd like to report something odd.
1651 * Just report the current state and copy the message.
1652 */
1653 chunk_printf(&trash, "agent warns : %s", wrn);
1654 set_server_check_status(check, status/*check->status*/, trash.area);
1655 }
1656 else
1657 set_server_check_status(check, status, NULL);
Willy Tarreau1746eec2014-04-25 10:46:47 +02001658
Christopher Faulet61cc8522020-04-20 14:54:42 +02001659 out:
1660 return ret;
Simon Horman5c942422013-11-25 10:46:32 +09001661
Christopher Faulet61cc8522020-04-20 14:54:42 +02001662 wait_more_data:
1663 ret = TCPCHK_EVAL_WAIT;
1664 goto out;
Simon Horman5c942422013-11-25 10:46:32 +09001665}
1666
Christopher Faulet61cc8522020-04-20 14:54:42 +02001667/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
1668 * connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1669 * TCPCHK_EVAL_STOP if an error occurred.
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001670 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001671static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
Willy Tarreau865c5142016-12-21 20:04:48 +01001672{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001673 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
1674 struct tcpcheck_connect *connect = &rule->connect;
1675 struct proxy *proxy = check->proxy;
1676 struct server *s = check->server;
1677 struct task *t = check->task;
1678 struct conn_stream *cs;
1679 struct connection *conn = NULL;
1680 struct protocol *proto;
1681 struct xprt_ops *xprt;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001682 struct tcpcheck_rule *next;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001683 int status, port;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001684
Christopher Faulet61cc8522020-04-20 14:54:42 +02001685 /* For a connect action we'll create a new connection. We may also have
1686 * to kill a previous one. But we don't want to leave *without* a
1687 * connection if we came here from the connection layer, hence with a
1688 * connection. Thus we'll proceed in the following order :
1689 * 1: close but not release previous connection (handled by the caller)
1690 * 2: try to get a new connection
1691 * 3: release and replace the old one on success
Willy Tarreau2c43a1e2007-10-14 23:05:39 +02001692 */
Willy Tarreaue7b73482013-11-21 11:50:50 +01001693
Christopher Faulet61cc8522020-04-20 14:54:42 +02001694 /* 2- prepare new connection */
1695 cs = cs_new(NULL);
1696 if (!cs) {
1697 chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
1698 tcpcheck_get_step_id(check, rule));
1699 if (rule->comment)
1700 chunk_appendf(&trash, " comment: '%s'", rule->comment);
1701 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
1702 ret = TCPCHK_EVAL_STOP;
1703 goto out;
1704 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001705
Christopher Faulet61cc8522020-04-20 14:54:42 +02001706 /* 3- release and replace the old one on success */
1707 if (check->cs) {
1708 if (check->wait_list.events)
Christopher Faulet06150e42020-04-27 11:22:56 +02001709 check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events,
1710 &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001711
1712 /* We may have been scheduled to run, and the I/O handler
1713 * expects to have a cs, so remove the tasklet
1714 */
1715 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1716 cs_destroy(check->cs);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001717 }
1718
Christopher Faulet61cc8522020-04-20 14:54:42 +02001719 tasklet_set_tid(check->wait_list.tasklet, tid);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001720
Christopher Faulet61cc8522020-04-20 14:54:42 +02001721 check->cs = cs;
1722 conn = cs->conn;
1723 conn_set_owner(conn, check->sess, NULL);
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001724
Christopher Faulet61cc8522020-04-20 14:54:42 +02001725 /* Maybe there were an older connection we were waiting on */
1726 check->wait_list.events = 0;
1727 conn->target = s ? &s->obj_type : &proxy->obj_type;
1728
1729 /* no client address */
1730 if (!sockaddr_alloc(&conn->dst)) {
1731 status = SF_ERR_RESOURCE;
1732 goto fail_check;
1733 }
1734
1735 /* connect to the connect rule addr if specified, otherwise the check
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001736 * addr if specified on the server. otherwise, use the server addr (it
1737 * MUST exist at this step).
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001738 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001739 *conn->dst = (is_addr(&connect->addr)
1740 ? connect->addr
1741 : (is_addr(&check->addr) ? check->addr : s->addr));
1742 proto = protocol_by_family(conn->dst->ss_family);
Simon Horman98637e52014-06-20 12:30:16 +09001743
Christopher Faulet61cc8522020-04-20 14:54:42 +02001744 port = 0;
1745 if (!port && connect->port)
1746 port = connect->port;
1747 if (!port && connect->port_expr) {
1748 struct sample *smp;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001749
Christopher Faulet61cc8522020-04-20 14:54:42 +02001750 smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
1751 SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
1752 connect->port_expr, SMP_T_SINT);
1753 if (smp)
1754 port = smp->data.u.sint;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001755 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001756 if (!port && is_inet_addr(&connect->addr))
1757 port = get_host_port(&connect->addr);
1758 if (!port && check->port)
1759 port = check->port;
1760 if (!port && is_inet_addr(&check->addr))
1761 port = get_host_port(&check->addr);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001762 if (!port) {
1763 /* The server MUST exist here */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001764 port = s->svc_port;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001765 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001766 set_host_port(conn->dst, port);
Willy Tarreau213c6782014-10-02 14:51:02 +02001767
Christopher Faulet61cc8522020-04-20 14:54:42 +02001768 xprt = ((connect->options & TCPCHK_OPT_SSL)
1769 ? xprt_get(XPRT_SSL)
1770 : ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) ? check->xprt : xprt_get(XPRT_RAW)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001771
Christopher Faulet61cc8522020-04-20 14:54:42 +02001772 conn_prepare(conn, proto, xprt);
1773 cs_attach(cs, check, &check_conn_cb);
Christopher Fauleta202d1d2020-03-26 17:38:49 +01001774
Christopher Faulet61cc8522020-04-20 14:54:42 +02001775 status = SF_ERR_INTERNAL;
Christopher Faulet2fabd9d2020-05-09 14:46:43 +02001776 next = get_next_tcpcheck_rule(check->tcpcheck_rules, rule);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001777 if (proto && proto->connect) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001778 int flags = 0;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001779
Christopher Faulet61cc8522020-04-20 14:54:42 +02001780 if (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK)
1781 flags |= CONNECT_HAS_DATA;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001782 if (!next || next->action != TCPCHK_ACT_EXPECT)
1783 flags |= CONNECT_DELACK_ALWAYS;
1784 status = proto->connect(conn, flags);
Christopher Faulet206368d2020-04-03 14:51:06 +02001785 }
1786
Christopher Faulet61cc8522020-04-20 14:54:42 +02001787 if (status != SF_ERR_NONE)
1788 goto fail_check;
Christopher Faulet799f3a42020-04-07 12:06:14 +02001789
Christopher Faulet61cc8522020-04-20 14:54:42 +02001790 conn->flags |= CO_FL_PRIVATE;
1791 conn->ctx = cs;
Christopher Faulet206368d2020-04-03 14:51:06 +02001792
Christopher Faulet61cc8522020-04-20 14:54:42 +02001793 /* The mux may be initialized now if there isn't server attached to the
1794 * check (email alerts) or if there is a mux proto specified or if there
1795 * is no alpn.
1796 */
Christopher Faulet12882cf2020-04-23 15:50:18 +02001797 if (!s || ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto) ||
1798 connect->mux_proto || (!connect->alpn && !check->alpn_str)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001799 const struct mux_ops *mux_ops;
Christopher Faulet206368d2020-04-03 14:51:06 +02001800
Christopher Faulet61cc8522020-04-20 14:54:42 +02001801 if (connect->mux_proto)
1802 mux_ops = connect->mux_proto->mux;
Christopher Faulet12882cf2020-04-23 15:50:18 +02001803 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001804 mux_ops = check->mux_proto->mux;
1805 else {
1806 int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
1807 ? PROTO_MODE_HTTP
1808 : PROTO_MODE_TCP);
1809
1810 mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
Christopher Faulet206368d2020-04-03 14:51:06 +02001811 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001812 if (mux_ops && conn_install_mux(conn, mux_ops, cs, proxy, check->sess) < 0) {
1813 status = SF_ERR_INTERNAL;
1814 goto fail_check;
1815 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001816 }
1817
Christopher Faulet61cc8522020-04-20 14:54:42 +02001818#ifdef USE_OPENSSL
1819 if (connect->sni)
1820 ssl_sock_set_servername(conn, connect->sni);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001821 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001822 ssl_sock_set_servername(conn, s->check.sni);
1823
1824 if (connect->alpn)
1825 ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001826 else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.alpn_str)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001827 ssl_sock_set_alpn(conn, (unsigned char *)s->check.alpn_str, s->check.alpn_len);
1828#endif
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001829 if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001830 conn->send_proxy_ofs = 1;
1831 conn->flags |= CO_FL_SOCKS4;
1832 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001833 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 +02001834 conn->send_proxy_ofs = 1;
1835 conn->flags |= CO_FL_SOCKS4;
Christopher Faulet206368d2020-04-03 14:51:06 +02001836 }
1837
Christopher Faulet61cc8522020-04-20 14:54:42 +02001838 if (connect->options & TCPCHK_OPT_SEND_PROXY) {
1839 conn->send_proxy_ofs = 1;
1840 conn->flags |= CO_FL_SEND_PROXY;
1841 }
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001842 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 +02001843 conn->send_proxy_ofs = 1;
1844 conn->flags |= CO_FL_SEND_PROXY;
Christopher Faulet206368d2020-04-03 14:51:06 +02001845 }
1846
Christopher Faulet61cc8522020-04-20 14:54:42 +02001847 if (conn_ctrl_ready(conn) && (connect->options & TCPCHK_OPT_LINGER)) {
1848 /* Some servers don't like reset on close */
1849 fdtab[cs->conn->handle.fd].linger_risk = 0;
1850 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001851
Christopher Faulet61cc8522020-04-20 14:54:42 +02001852 if (conn_ctrl_ready(conn) && (conn->flags & (CO_FL_SEND_PROXY | CO_FL_SOCKS4))) {
1853 if (xprt_add_hs(conn) < 0)
1854 status = SF_ERR_RESOURCE;
1855 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001856
Christopher Faulet61cc8522020-04-20 14:54:42 +02001857 fail_check:
1858 /* It can return one of :
1859 * - SF_ERR_NONE if everything's OK
1860 * - SF_ERR_SRVTO if there are no more servers
1861 * - SF_ERR_SRVCL if the connection was refused by the server
1862 * - SF_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
1863 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1864 * - SF_ERR_INTERNAL for any other purely internal errors
1865 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1866 * Note that we try to prevent the network stack from sending the ACK during the
1867 * connect() when a pure TCP check is used (without PROXY protocol).
1868 */
1869 switch (status) {
1870 case SF_ERR_NONE:
1871 /* we allow up to min(inter, timeout.connect) for a connection
1872 * to establish but only when timeout.check is set as it may be
1873 * to short for a full check otherwise
1874 */
1875 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet206368d2020-04-03 14:51:06 +02001876
Christopher Faulet61cc8522020-04-20 14:54:42 +02001877 if (proxy->timeout.check && proxy->timeout.connect) {
1878 int t_con = tick_add(now_ms, proxy->timeout.connect);
1879 t->expire = tick_first(t->expire, t_con);
1880 }
1881 break;
1882 case SF_ERR_SRVTO: /* ETIMEDOUT */
1883 case SF_ERR_SRVCL: /* ECONNREFUSED, ENETUNREACH, ... */
1884 case SF_ERR_PRXCOND:
1885 case SF_ERR_RESOURCE:
1886 case SF_ERR_INTERNAL:
1887 chk_report_conn_err(check, errno, 0);
1888 ret = TCPCHK_EVAL_STOP;
1889 goto out;
Christopher Faulet206368d2020-04-03 14:51:06 +02001890 }
1891
Christopher Faulet61cc8522020-04-20 14:54:42 +02001892 /* don't do anything until the connection is established */
1893 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet4b3a2df2020-05-12 15:05:43 +02001894 if (conn->mux) {
1895 if (next && next->action == TCPCHK_ACT_SEND)
1896 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
1897 else
1898 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
1899 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001900 ret = TCPCHK_EVAL_WAIT;
1901 goto out;
1902 }
1903
1904 out:
1905 if (conn && check->result == CHK_RES_FAILED)
1906 conn->flags |= CO_FL_ERROR;
1907 return ret;
Christopher Faulet206368d2020-04-03 14:51:06 +02001908}
1909
Christopher Faulet61cc8522020-04-20 14:54:42 +02001910/* Evaluates a TCPCHK_ACT_SEND rule. Returns TCPCHK_EVAL_WAIT if outgoing data
1911 * were not fully sent, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
1912 * TCPCHK_EVAL_STOP if an error occurred.
1913 */
1914static enum tcpcheck_eval_ret tcpcheck_eval_send(struct check *check, struct tcpcheck_rule *rule)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001915{
1916 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001917 struct tcpcheck_send *send = &rule->send;
1918 struct conn_stream *cs = check->cs;
1919 struct connection *conn = cs_conn(cs);
1920 struct buffer *tmp = NULL;
1921 struct htx *htx = NULL;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001922
Christopher Faulet61cc8522020-04-20 14:54:42 +02001923 /* reset the read & write buffer */
1924 b_reset(&check->bi);
1925 b_reset(&check->bo);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001926
Christopher Faulet61cc8522020-04-20 14:54:42 +02001927 switch (send->type) {
1928 case TCPCHK_SEND_STRING:
1929 case TCPCHK_SEND_BINARY:
1930 if (istlen(send->data) >= b_size(&check->bo)) {
1931 chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
1932 (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
1933 tcpcheck_get_step_id(check, rule));
1934 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
1935 ret = TCPCHK_EVAL_STOP;
1936 goto out;
1937 }
1938 b_putist(&check->bo, send->data);
1939 break;
1940 case TCPCHK_SEND_STRING_LF:
1941 check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
1942 if (!b_data(&check->bo))
1943 goto out;
1944 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001945 case TCPCHK_SEND_BINARY_LF: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02001946 int len = b_size(&check->bo);
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001947
Christopher Faulet61cc8522020-04-20 14:54:42 +02001948 tmp = alloc_trash_chunk();
1949 if (!tmp)
1950 goto error_lf;
1951 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
1952 if (!b_data(tmp))
1953 goto out;
1954 tmp->area[tmp->data] = '\0';
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001955 if (parse_binary(b_orig(tmp), &check->bo.area, &len, NULL) == 0)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001956 goto error_lf;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001957 check->bo.data = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001958 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001959 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001960 case TCPCHK_SEND_HTTP: {
1961 struct htx_sl *sl;
1962 struct ist meth, uri, vsn, clen, body;
1963 unsigned int slflags = 0;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001964
Christopher Faulet61cc8522020-04-20 14:54:42 +02001965 tmp = alloc_trash_chunk();
1966 if (!tmp)
1967 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001968
Christopher Faulet61cc8522020-04-20 14:54:42 +02001969 meth = ((send->http.meth.meth == HTTP_METH_OTHER)
1970 ? ist2(send->http.meth.str.area, send->http.meth.str.data)
1971 : http_known_methods[send->http.meth.meth]);
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02001972 if (send->http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
1973 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.uri_fmt);
1974 uri = (b_data(tmp) ? ist2(b_orig(tmp), b_data(tmp)) : ist("/"));
1975 }
1976 else
1977 uri = (isttest(send->http.uri) ? send->http.uri : ist("/"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001978 vsn = (isttest(send->http.vsn) ? send->http.vsn : ist("HTTP/1.0"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001979
Christopher Faulet8bf8fda2020-04-28 09:10:19 +02001980 if ((istlen(vsn) == 6 && *(vsn.ptr+5) == '2') ||
1981 (istlen(vsn) == 8 && (*(vsn.ptr+5) > '1' || (*(vsn.ptr+5) == '1' && *(vsn.ptr+7) >= '1'))))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001982 slflags |= HTX_SL_F_VER_11;
1983 slflags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
1984 if (!isttest(send->http.body))
1985 slflags |= HTX_SL_F_BODYLESS;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001986
Christopher Faulet61cc8522020-04-20 14:54:42 +02001987 htx = htx_from_buf(&check->bo);
1988 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, slflags, meth, uri, vsn);
1989 if (!sl)
1990 goto error_htx;
1991 sl->info.req.meth = send->http.meth.meth;
Christopher Faulet39959a72020-04-29 13:21:37 +02001992 if (!http_update_host(htx, sl, uri))
1993 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001994
Christopher Faulet61cc8522020-04-20 14:54:42 +02001995 if (!LIST_ISEMPTY(&send->http.hdrs)) {
1996 struct tcpcheck_http_hdr *hdr;
Christopher Faulet39959a72020-04-29 13:21:37 +02001997 struct ist hdr_value;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001998
Christopher Faulet61cc8522020-04-20 14:54:42 +02001999 list_for_each_entry(hdr, &send->http.hdrs, list) {
2000 chunk_reset(tmp);
2001 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &hdr->value);
2002 if (!b_data(tmp))
2003 continue;
Christopher Faulet39959a72020-04-29 13:21:37 +02002004 hdr_value = ist2(b_orig(tmp), b_data(tmp));
2005 if (!htx_add_header(htx, hdr->name, hdr_value))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002006 goto error_htx;
Christopher Faulet39959a72020-04-29 13:21:37 +02002007 if ((sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(hdr->name, ist("host"))) {
2008 if (!http_update_authority(htx, sl, hdr_value))
2009 goto error_htx;
2010 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002011 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002012
Christopher Faulet61cc8522020-04-20 14:54:42 +02002013 }
2014 if (check->proxy->options2 & PR_O2_CHK_SNDST) {
2015 chunk_reset(tmp);
2016 httpchk_build_status_header(check->server, tmp);
2017 if (!htx_add_header(htx, ist("X-Haproxy-Server-State"), ist2(b_orig(tmp), b_data(tmp))))
2018 goto error_htx;
2019 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002020
2021
2022 if (send->http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
2023 chunk_reset(tmp);
2024 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &send->http.body_fmt);
2025 body = ist2(b_orig(tmp), b_data(tmp));
2026 }
2027 else
2028 body = send->http.body;
2029 clen = ist((!istlen(body) ? "0" : ultoa(istlen(body))));
2030
2031 if (!htx_add_header(htx, ist("Connection"), ist("close")) ||
2032 !htx_add_header(htx, ist("Content-length"), clen))
2033 goto error_htx;
2034
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002035
Christopher Faulet61cc8522020-04-20 14:54:42 +02002036 if (!htx_add_endof(htx, HTX_BLK_EOH) ||
Christopher Faulet574e7bd2020-05-06 15:38:58 +02002037 (istlen(body) && !htx_add_data_atonce(htx, body)) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002038 !htx_add_endof(htx, HTX_BLK_EOM))
2039 goto error_htx;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002040
Christopher Faulet61cc8522020-04-20 14:54:42 +02002041 htx_to_buf(htx, &check->bo);
2042 break;
2043 }
2044 case TCPCHK_SEND_UNDEF:
2045 /* Should never happen. */
2046 ret = TCPCHK_EVAL_STOP;
2047 goto out;
2048 };
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002049
Christopher Faulet6d471212020-04-22 11:09:25 +02002050
2051 if (conn->mux->snd_buf(cs, &check->bo,
2052 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0) <= 0) {
Christopher Faulet815516d2020-04-21 13:02:14 +02002053 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002054 ret = TCPCHK_EVAL_STOP;
Christopher Faulet815516d2020-04-21 13:02:14 +02002055 goto out;
2056 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002057 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002058 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002059 cs->conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
2060 ret = TCPCHK_EVAL_WAIT;
2061 goto out;
2062 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002063
Christopher Faulet61cc8522020-04-20 14:54:42 +02002064 out:
2065 free_trash_chunk(tmp);
2066 return ret;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002067
Christopher Faulet61cc8522020-04-20 14:54:42 +02002068 error_htx:
2069 if (htx) {
2070 htx_reset(htx);
2071 htx_to_buf(htx, &check->bo);
2072 }
2073 chunk_printf(&trash, "tcp-check send : failed to build HTTP request at step %d",
2074 tcpcheck_get_step_id(check, rule));
2075 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2076 ret = TCPCHK_EVAL_STOP;
2077 goto out;
2078
2079 error_lf:
2080 chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
2081 tcpcheck_get_step_id(check, rule));
2082 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2083 ret = TCPCHK_EVAL_STOP;
2084 goto out;
2085
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002086}
2087
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002088/* Try to receive data before evaluating a tcp-check expect rule. Returns
2089 * TCPCHK_EVAL_WAIT if it is already subscribed on receive events or if nothing
Christopher Faulet61cc8522020-04-20 14:54:42 +02002090 * was received, TCPCHK_EVAL_CONTINUE to evaluate the expect rule or
2091 * TCPCHK_EVAL_STOP if an error occurred.
2092 */
2093static enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_rule *rule)
Christopher Faulet1997eca2020-04-03 23:13:50 +02002094{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002095 struct conn_stream *cs = check->cs;
2096 struct connection *conn = cs_conn(cs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002097 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002098 size_t max, read, cur_read = 0;
2099 int is_empty;
2100 int read_poll = MAX_READ_POLL_LOOPS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002101
Christopher Faulet61cc8522020-04-20 14:54:42 +02002102 if (check->wait_list.events & SUB_RETRY_RECV)
2103 goto wait_more_data;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002104
Christopher Faulet61cc8522020-04-20 14:54:42 +02002105 if (cs->flags & CS_FL_EOS)
2106 goto end_recv;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002107
Christopher Faulet61cc8522020-04-20 14:54:42 +02002108 /* errors on the connection and the conn-stream were already checked */
Christopher Faulet1997eca2020-04-03 23:13:50 +02002109
Christopher Faulet61cc8522020-04-20 14:54:42 +02002110 /* prepare to detect if the mux needs more room */
2111 cs->flags &= ~CS_FL_WANT_ROOM;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002112
Christopher Faulet61cc8522020-04-20 14:54:42 +02002113 while ((cs->flags & CS_FL_RCV_MORE) ||
2114 (!(conn->flags & CO_FL_ERROR) && !(cs->flags & (CS_FL_ERROR|CS_FL_EOS)))) {
2115 max = (IS_HTX_CS(cs) ? htx_free_space(htxbuf(&check->bi)) : b_room(&check->bi));
2116 read = conn->mux->rcv_buf(cs, &check->bi, max, 0);
2117 cur_read += read;
2118 if (!read ||
2119 (cs->flags & CS_FL_WANT_ROOM) ||
2120 (--read_poll <= 0) ||
2121 (read < max && read >= global.tune.recv_enough))
2122 break;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002123 }
2124
Christopher Faulet61cc8522020-04-20 14:54:42 +02002125 end_recv:
2126 is_empty = (IS_HTX_CS(cs) ? htx_is_empty(htxbuf(&check->bi)) : !b_data(&check->bi));
2127 if (is_empty && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) {
2128 /* Report network errors only if we got no other data. Otherwise
2129 * we'll let the upper layers decide whether the response is OK
2130 * or not. It is very common that an RST sent by the server is
2131 * reported as an error just after the last data chunk.
2132 */
2133 goto stop;
2134 }
2135 if (!cur_read) {
2136 if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
2137 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
2138 goto wait_more_data;
2139 }
2140 if (is_empty) {
Christopher Faulet1941bab2020-05-05 07:55:50 +02002141 int status;
2142
Christopher Faulet61cc8522020-04-20 14:54:42 +02002143 chunk_printf(&trash, "TCPCHK got an empty response at step %d",
2144 tcpcheck_get_step_id(check, rule));
2145 if (rule->comment)
2146 chunk_appendf(&trash, " comment: '%s'", rule->comment);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002147
2148 status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7RSP);
2149 set_server_check_status(check, status, trash.area);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002150 goto stop;
2151 }
2152 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02002153
2154 out:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002155 return ret;
2156
Christopher Faulet61cc8522020-04-20 14:54:42 +02002157 stop:
Christopher Faulet1997eca2020-04-03 23:13:50 +02002158 ret = TCPCHK_EVAL_STOP;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002159 goto out;
2160
2161 wait_more_data:
2162 ret = TCPCHK_EVAL_WAIT;
2163 goto out;
2164}
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002165
Christopher Faulet61cc8522020-04-20 14:54:42 +02002166/* Evaluates an HTTP TCPCHK_ACT_EXPECT rule. If <last_read> is set , no more data
2167 * are expected. Returns TCPCHK_EVAL_WAIT to wait for more data,
2168 * TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP if an
2169 * error occurred.
2170 */
2171static 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 +02002172{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002173 struct htx *htx = htxbuf(&check->bi);
2174 struct htx_sl *sl;
2175 struct htx_blk *blk;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002176 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002177 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002178 struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02002179 enum healthcheck_status status = HCHK_STATUS_L7RSP;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002180 struct ist desc = IST_NULL;
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002181 int i, match, inverse;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002182
Christopher Faulet61cc8522020-04-20 14:54:42 +02002183 last_read |= (!htx_free_space(htx) || (htx_get_tail_type(htx) == HTX_BLK_EOM));
Christopher Faulet267b01b2020-04-04 10:27:09 +02002184
Christopher Faulet61cc8522020-04-20 14:54:42 +02002185 if (htx->flags & HTX_FL_PARSING_ERROR) {
2186 status = HCHK_STATUS_L7RSP;
2187 goto error;
2188 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002189
Christopher Faulet61cc8522020-04-20 14:54:42 +02002190 if (htx_is_empty(htx)) {
2191 if (last_read) {
2192 status = HCHK_STATUS_L7RSP;
2193 goto error;
2194 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002195 goto wait_more_data;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002196 }
Christopher Faulet267b01b2020-04-04 10:27:09 +02002197
Christopher Faulet61cc8522020-04-20 14:54:42 +02002198 sl = http_get_stline(htx);
2199 check->code = sl->info.res.status;
2200
2201 if (check->server &&
2202 (check->server->proxy->options & PR_O_DISABLE404) &&
2203 (check->server->next_state != SRV_ST_STOPPED) &&
2204 (check->code == 404)) {
2205 /* 404 may be accepted as "stopping" only if the server was up */
2206 goto out;
2207 }
2208
2209 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2210 /* Make GCC happy ; initialize match to a failure state. */
2211 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002212 status = expect->err_status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002213
2214 switch (expect->type) {
2215 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +02002216 match = 0;
2217 for (i = 0; i < expect->codes.num; i++) {
2218 if (sl->info.res.status >= expect->codes.codes[i][0] &&
2219 sl->info.res.status <= expect->codes.codes[i][1]) {
2220 match = 1;
2221 break;
2222 }
2223 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002224
2225 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002226 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002227 if (LIST_ISEMPTY(&expect->onerror_fmt))
2228 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002229 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002230 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002231 match = regex_exec2(expect->regex, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
2232
2233 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002234 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002235 if (LIST_ISEMPTY(&expect->onerror_fmt))
2236 desc = htx_sl_res_reason(sl);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002237 break;
2238
Christopher Faulet39708192020-05-05 10:47:36 +02002239 case TCPCHK_EXPECT_HTTP_HEADER: {
2240 struct http_hdr_ctx ctx;
2241 struct ist npat, vpat, value;
2242 int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
2243
2244 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
2245 nbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002246 if (!nbuf) {
2247 status = HCHK_STATUS_L7RSP;
2248 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002249 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002250 }
Christopher Faulet39708192020-05-05 10:47:36 +02002251 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 +02002252 if (!b_data(nbuf)) {
2253 status = HCHK_STATUS_L7RSP;
2254 desc = ist("log-format string evaluated to an empty string");
2255 goto error;
2256 }
Christopher Faulet39708192020-05-05 10:47:36 +02002257 npat = ist2(b_orig(nbuf), b_data(nbuf));
2258 }
2259 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
2260 npat = expect->hdr.name;
2261
2262 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
2263 vbuf = alloc_trash_chunk();
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002264 if (!vbuf) {
2265 status = HCHK_STATUS_L7RSP;
2266 desc = ist("Failed to allocate buffer to eval log-format string");
Christopher Faulet39708192020-05-05 10:47:36 +02002267 goto error;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002268 }
Christopher Faulet39708192020-05-05 10:47:36 +02002269 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 +02002270 if (!b_data(vbuf)) {
2271 status = HCHK_STATUS_L7RSP;
2272 desc = ist("log-format string evaluated to an empty string");
2273 goto error;
2274 }
Christopher Faulet39708192020-05-05 10:47:36 +02002275 vpat = ist2(b_orig(vbuf), b_data(vbuf));
2276 }
2277 else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
2278 vpat = expect->hdr.value;
2279
2280 match = 0;
2281 ctx.blk = NULL;
2282 while (1) {
2283 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
2284 case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
2285 if (!http_find_str_header(htx, npat, &ctx, full))
2286 goto end_of_match;
2287 break;
2288 case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
2289 if (!http_find_pfx_header(htx, npat, &ctx, full))
2290 goto end_of_match;
2291 break;
2292 case TCPCHK_EXPT_FL_HTTP_HNAME_END:
2293 if (!http_find_sfx_header(htx, npat, &ctx, full))
2294 goto end_of_match;
2295 break;
2296 case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
2297 if (!http_find_sub_header(htx, npat, &ctx, full))
2298 goto end_of_match;
2299 break;
2300 case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
2301 if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
2302 goto end_of_match;
2303 break;
Christopher Faulet083eff32020-05-07 15:41:39 +02002304 default:
2305 /* should never happen */
2306 goto end_of_match;
Christopher Faulet39708192020-05-05 10:47:36 +02002307 }
2308
Christopher Faulet083eff32020-05-07 15:41:39 +02002309 /* A header has matched the name pattern, let's test its
2310 * value now (always defined from there). If there is no
2311 * value pattern, it is a good match.
2312 */
2313
Christopher Faulet39708192020-05-05 10:47:36 +02002314 if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
2315 match = 1;
2316 goto end_of_match;
2317 }
2318
2319 value = ctx.value;
2320 switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
2321 case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
2322 if (isteq(value, vpat)) {
2323 match = 1;
2324 goto end_of_match;
2325 }
2326 break;
2327 case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
2328 if (istlen(value) < istlen(vpat))
2329 break;
2330 value = ist2(istptr(value), istlen(vpat));
2331 if (isteq(value, vpat)) {
2332 match = 1;
2333 goto end_of_match;
2334 }
2335 break;
2336 case TCPCHK_EXPT_FL_HTTP_HVAL_END:
2337 if (istlen(value) < istlen(vpat))
2338 break;
2339 value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
2340 if (isteq(value, vpat)) {
2341 match = 1;
2342 goto end_of_match;
2343 }
2344 break;
2345 case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
2346 if (isttest(istist(value, vpat))) {
2347 match = 1;
2348 goto end_of_match;
2349 }
2350 break;
2351 case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
2352 if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
2353 match = 1;
2354 goto end_of_match;
2355 }
2356 break;
2357 }
2358 }
2359
2360 end_of_match:
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002361 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7STS);
Christopher Faulet39708192020-05-05 10:47:36 +02002362 if (LIST_ISEMPTY(&expect->onerror_fmt))
2363 desc = htx_sl_res_reason(sl);
2364 break;
2365 }
2366
Christopher Faulet61cc8522020-04-20 14:54:42 +02002367 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Faulet67a23452020-05-05 18:10:01 +02002368 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002369 case TCPCHK_EXPECT_HTTP_BODY_LF:
2370 match = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002371 chunk_reset(&trash);
2372 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
2373 enum htx_blk_type type = htx_get_blk_type(blk);
2374
2375 if (type == HTX_BLK_EOM || type == HTX_BLK_TLR || type == HTX_BLK_EOT)
2376 break;
2377 if (type == HTX_BLK_DATA) {
2378 if (!chunk_istcat(&trash, htx_get_blk_value(htx, blk)))
2379 break;
2380 }
2381 }
2382
2383 if (!b_data(&trash)) {
2384 if (!last_read)
2385 goto wait_more_data;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002386 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002387 if (LIST_ISEMPTY(&expect->onerror_fmt))
2388 desc = ist("HTTP content check could not find a response body");
Christopher Faulet61cc8522020-04-20 14:54:42 +02002389 goto error;
2390 }
2391
Christopher Fauletaaab0832020-05-05 15:54:22 +02002392 if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
2393 tmp = alloc_trash_chunk();
2394 if (!tmp) {
2395 status = HCHK_STATUS_L7RSP;
2396 desc = ist("Failed to allocate buffer to eval log-format string");
2397 goto error;
2398 }
2399 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2400 if (!b_data(tmp)) {
2401 status = HCHK_STATUS_L7RSP;
2402 desc = ist("log-format string evaluated to an empty string");
2403 goto error;
2404 }
2405 }
2406
Christopher Faulet61cc8522020-04-20 14:54:42 +02002407 if (!last_read &&
2408 ((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
Christopher Fauletaaab0832020-05-05 15:54:22 +02002409 ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
Christopher Faulet61cc8522020-04-20 14:54:42 +02002410 (expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
2411 ret = TCPCHK_EVAL_WAIT;
2412 goto out;
2413 }
2414
2415 if (expect->type ==TCPCHK_EXPECT_HTTP_BODY)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002416 match = my_memmem(b_orig(&trash), b_data(&trash), istptr(expect->data), istlen(expect->data)) != NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002417 else if (expect->type ==TCPCHK_EXPECT_HTTP_BODY_LF)
2418 match = my_memmem(b_orig(&trash), b_data(&trash), b_orig(tmp), b_data(tmp)) != NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002419 else
2420 match = regex_exec2(expect->regex, b_orig(&trash), b_data(&trash));
2421
2422 /* Set status and description in case of error */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002423 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002424 if (LIST_ISEMPTY(&expect->onerror_fmt))
2425 desc = (inverse
2426 ? ist("HTTP check matched unwanted content")
2427 : ist("HTTP content check did not match"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002428 break;
2429
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002430
Christopher Faulet61cc8522020-04-20 14:54:42 +02002431 default:
2432 /* should never happen */
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002433 status = ((status != HCHK_STATUS_UNKNOWN) ? status : HCHK_STATUS_L7RSP);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002434 goto error;
2435 }
2436
Christopher Faulet61cc8522020-04-20 14:54:42 +02002437 /* Wait for more data on mismatch only if no minimum is defined (-1),
2438 * otherwise the absence of match is already conclusive.
2439 */
2440 if (!match && !last_read && (expect->min_recv == -1)) {
2441 ret = TCPCHK_EVAL_WAIT;
2442 goto out;
2443 }
2444
2445 if (!(match ^ inverse))
2446 goto error;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002447
2448 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002449 free_trash_chunk(tmp);
Christopher Faulet39708192020-05-05 10:47:36 +02002450 free_trash_chunk(nbuf);
2451 free_trash_chunk(vbuf);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002452 free_trash_chunk(msg);
2453 return ret;
2454
2455 error:
2456 ret = TCPCHK_EVAL_STOP;
2457 msg = alloc_trash_chunk();
2458 if (msg)
Christopher Faulet61cc8522020-04-20 14:54:42 +02002459 tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002460 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
2461 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002462
2463 wait_more_data:
2464 ret = TCPCHK_EVAL_WAIT;
2465 goto out;
2466}
2467
Christopher Faulet61cc8522020-04-20 14:54:42 +02002468/* Evaluates a TCP TCPCHK_ACT_EXPECT rule. Returns TCPCHK_EVAL_WAIT to wait for
2469 * more data, TCPCHK_EVAL_CONTINUE to evaluate the next rule or TCPCHK_EVAL_STOP
2470 * if an error occurred.
2471 */
2472static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct tcpcheck_rule *rule, int last_read)
2473{
2474 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2475 struct tcpcheck_expect *expect = &rule->expect;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002476 struct buffer *msg = NULL, *tmp = NULL;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002477 struct ist desc = IST_NULL;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002478 enum healthcheck_status status;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002479 int match, inverse;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002480
Christopher Faulet61cc8522020-04-20 14:54:42 +02002481 last_read |= b_full(&check->bi);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002482
Christopher Faulet61cc8522020-04-20 14:54:42 +02002483 /* The current expect might need more data than the previous one, check again
2484 * that the minimum amount data required to match is respected.
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002485 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02002486 if (!last_read) {
2487 if ((expect->type == TCPCHK_EXPECT_STRING || expect->type == TCPCHK_EXPECT_BINARY) &&
2488 (b_data(&check->bi) < istlen(expect->data))) {
2489 ret = TCPCHK_EVAL_WAIT;
2490 goto out;
2491 }
2492 if (expect->min_recv > 0 && (b_data(&check->bi) < expect->min_recv)) {
2493 ret = TCPCHK_EVAL_WAIT;
2494 goto out;
2495 }
2496 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002497
Christopher Faulet61cc8522020-04-20 14:54:42 +02002498 inverse = !!(expect->flags & TCPCHK_EXPT_FL_INV);
2499 /* Make GCC happy ; initialize match to a failure state. */
2500 match = inverse;
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002501 status = ((expect->err_status != HCHK_STATUS_UNKNOWN) ? expect->err_status : HCHK_STATUS_L7RSP);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002502
Christopher Faulet61cc8522020-04-20 14:54:42 +02002503 switch (expect->type) {
2504 case TCPCHK_EXPECT_STRING:
2505 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +02002506 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 +02002507 break;
Christopher Faulet67a23452020-05-05 18:10:01 +02002508 case TCPCHK_EXPECT_STRING_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02002509 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 +02002510 break;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002511
Christopher Faulet67a23452020-05-05 18:10:01 +02002512 case TCPCHK_EXPECT_BINARY_REGEX:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002513 chunk_reset(&trash);
2514 dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
Christopher Faulet88d939c2020-04-22 15:32:11 +02002515 match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002516 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02002517
2518 case TCPCHK_EXPECT_STRING_LF:
2519 case TCPCHK_EXPECT_BINARY_LF:
2520 match = 0;
2521 tmp = alloc_trash_chunk();
2522 if (!tmp) {
2523 status = HCHK_STATUS_L7RSP;
2524 desc = ist("Failed to allocate buffer to eval format string");
2525 goto error;
2526 }
2527 tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
2528 if (!b_data(tmp)) {
2529 status = HCHK_STATUS_L7RSP;
2530 desc = ist("log-format string evaluated to an empty string");
2531 goto error;
2532 }
2533 if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
2534 int len = tmp->data;
2535 if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
2536 status = HCHK_STATUS_L7RSP;
2537 desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
2538 goto error;
2539 }
2540 tmp->data = len;
2541 }
2542 if (b_data(&check->bi) < tmp->data) {
2543 if (!last_read) {
2544 ret = TCPCHK_EVAL_WAIT;
2545 goto out;
2546 }
2547 break;
2548 }
2549 match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
2550 break;
2551
Christopher Faulet61cc8522020-04-20 14:54:42 +02002552 case TCPCHK_EXPECT_CUSTOM:
2553 if (expect->custom)
2554 ret = expect->custom(check, rule, last_read);
2555 goto out;
2556 default:
2557 /* Should never happen. */
2558 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002559 goto out;
2560 }
2561
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002562
Christopher Faulet61cc8522020-04-20 14:54:42 +02002563 /* Wait for more data on mismatch only if no minimum is defined (-1),
2564 * otherwise the absence of match is already conclusive.
2565 */
2566 if (!match && !last_read && (expect->min_recv == -1)) {
2567 ret = TCPCHK_EVAL_WAIT;
2568 goto out;
2569 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002570
Christopher Faulet61cc8522020-04-20 14:54:42 +02002571 /* Result as expected, next rule. */
2572 if (match ^ inverse)
2573 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002574
Christopher Fauletaaab0832020-05-05 15:54:22 +02002575 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002576 /* From this point on, we matched something we did not want, this is an error state. */
2577 ret = TCPCHK_EVAL_STOP;
2578 msg = alloc_trash_chunk();
2579 if (msg)
Christopher Faulet0d6909b2020-05-05 15:50:37 +02002580 tcpcheck_expect_onerror_message(msg, check, rule, match, desc);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002581 set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002582 free_trash_chunk(msg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002583
Christopher Faulet61cc8522020-04-20 14:54:42 +02002584 out:
Christopher Fauletaaab0832020-05-05 15:54:22 +02002585 free_trash_chunk(tmp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002586 return ret;
2587}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002588
Christopher Faulet61cc8522020-04-20 14:54:42 +02002589/* Evaluates a TCPCHK_ACT_ACTION_KW rule. Returns TCPCHK_EVAL_CONTINUE to
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002590 * evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred. It never
Christopher Faulet61cc8522020-04-20 14:54:42 +02002591 * waits.
2592 */
2593static enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpcheck_rule *rule)
2594{
2595 enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
2596 struct act_rule *act_rule;
2597 enum act_return act_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002598
Christopher Faulet61cc8522020-04-20 14:54:42 +02002599 act_rule =rule->action_kw.rule;
2600 act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0);
2601 if (act_ret != ACT_RET_CONT) {
2602 chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n",
2603 tcpcheck_get_step_id(check, rule));
2604 set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
2605 ret = TCPCHK_EVAL_STOP;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002606 }
2607
Christopher Faulet61cc8522020-04-20 14:54:42 +02002608 return ret;
2609}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002610
Christopher Faulet61cc8522020-04-20 14:54:42 +02002611/* Executes a tcp-check ruleset. Note that this is called both from the
2612 * connection's wake() callback and from the check scheduling task. It returns
2613 * 0 on normal cases, or <0 if a close() has happened on an existing connection,
2614 * presenting the risk of an fd replacement.
2615 *
2616 * Please do NOT place any return statement in this function and only leave
2617 * via the out_end_tcpcheck label after setting retcode.
2618 */
2619static int tcpcheck_main(struct check *check)
2620{
2621 struct tcpcheck_rule *rule;
2622 struct conn_stream *cs = check->cs;
2623 struct connection *conn = cs_conn(cs);
2624 int must_read = 1, last_read = 0;
2625 int ret, retcode = 0;
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002626 enum tcpcheck_eval_ret eval_ret;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002627
Christopher Faulet61cc8522020-04-20 14:54:42 +02002628 /* here, we know that the check is complete or that it failed */
2629 if (check->result != CHK_RES_UNKNOWN)
Christopher Fauletb693a0d2020-04-27 15:59:22 +02002630 goto out;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002631
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002632 /* Note: the conn-stream and the connection may only be undefined before
2633 * the first rule evaluation (it is always a connect rule) or when the
2634 * conn-stream allocation failed on the first connect.
2635 */
2636
Christopher Faulet61cc8522020-04-20 14:54:42 +02002637 /* 1- check for connection error, if any */
2638 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2639 goto out_end_tcpcheck;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002640
Christopher Faulet61cc8522020-04-20 14:54:42 +02002641 /* 2- check if we are waiting for the connection establishment. It only
2642 * happens during TCPCHK_ACT_CONNECT. */
2643 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002644 if (conn->flags & CO_FL_WAIT_XPRT) {
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002645 struct tcpcheck_rule *next;
2646
2647 next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step);
2648 if (next && next->action == TCPCHK_ACT_SEND) {
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002649 if (!(check->wait_list.events & SUB_RETRY_SEND))
2650 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002651 goto out;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002652 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002653 else {
Christopher Faulet030ed4b2020-05-28 13:58:34 +02002654 eval_ret = tcpcheck_eval_recv(check, check->current_step);
2655 if (eval_ret == TCPCHK_EVAL_STOP)
2656 goto out_end_tcpcheck;
2657 else if (eval_ret == TCPCHK_EVAL_WAIT)
2658 goto out;
2659 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2660 must_read = 0;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002661 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002662 }
Christopher Faulet97b2a8b2020-05-09 17:34:43 +02002663 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002664 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002665
2666 /* 3- check for pending outgoing data. It only happens during
2667 * TCPCHK_ACT_SEND. */
2668 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002669 if (b_data(&check->bo)) {
Christopher Faulet07321342020-05-09 17:37:43 +02002670 /* We're already waiting to be able to send, give up */
2671 if (check->wait_list.events & SUB_RETRY_SEND)
2672 goto out;
2673
Christopher Faulet6d471212020-04-22 11:09:25 +02002674 ret = conn->mux->snd_buf(cs, &check->bo,
2675 (IS_HTX_CONN(conn) ? (htxbuf(&check->bo))->data: b_data(&check->bo)), 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002676 if (ret <= 0) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002677 if ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))
Christopher Faulet61cc8522020-04-20 14:54:42 +02002678 goto out_end_tcpcheck;
Christopher Faulet61cc8522020-04-20 14:54:42 +02002679 }
Christopher Faulet6d471212020-04-22 11:09:25 +02002680 if ((IS_HTX_CONN(conn) && !htx_is_empty(htxbuf(&check->bo))) || b_data(&check->bo)) {
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002681 conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002682 goto out;
2683 }
2684 }
2685 rule = LIST_NEXT(&check->current_step->list, typeof(rule), list);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002686 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002687
Christopher Faulet61cc8522020-04-20 14:54:42 +02002688 /* 4- check if a rule must be resume. It happens if check->current_step
2689 * is defined. */
2690 else if (check->current_step)
2691 rule = check->current_step;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002692
Christopher Faulet61cc8522020-04-20 14:54:42 +02002693 /* 5- It is the first evaluation. We must create a session and preset
2694 * tcp-check variables */
2695 else {
2696 struct tcpcheck_var *var;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002697
Christopher Faulet61cc8522020-04-20 14:54:42 +02002698 /* First evaluation, create a session */
2699 check->sess = session_new(&checks_fe, NULL, &check->obj_type);
2700 if (!check->sess) {
2701 chunk_printf(&trash, "TCPCHK error allocating check session");
2702 set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
2703 goto out_end_tcpcheck;
2704 }
2705 vars_init(&check->vars, SCOPE_CHECK);
2706 rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
Willy Tarreauef953952014-10-02 14:30:14 +02002707
Christopher Faulet61cc8522020-04-20 14:54:42 +02002708 /* Preset tcp-check variables */
2709 list_for_each_entry(var, &check->tcpcheck_rules->preset_vars, list) {
2710 struct sample smp;
Willy Tarreau449f9522015-05-13 15:39:48 +02002711
Christopher Faulet61cc8522020-04-20 14:54:42 +02002712 memset(&smp, 0, sizeof(smp));
2713 smp_set_owner(&smp, check->proxy, check->sess, NULL, SMP_OPT_FINAL);
2714 smp.data = var->data;
Christopher Fauletb61caf42020-04-21 10:57:42 +02002715 vars_set_by_name_ifexist(istptr(var->name), istlen(var->name), &smp);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002716 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02002717 }
2718
Christopher Faulet61cc8522020-04-20 14:54:42 +02002719 /* Now evaluate the tcp-check rules */
Willy Tarreaudeccd112018-06-14 18:38:55 +02002720
Christopher Faulet61cc8522020-04-20 14:54:42 +02002721 list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002722 check->code = 0;
2723 switch (rule->action) {
2724 case TCPCHK_ACT_CONNECT:
2725 check->current_step = rule;
Willy Tarreauabca5b62013-12-06 14:19:25 +01002726
Christopher Faulet61cc8522020-04-20 14:54:42 +02002727 /* close but not release yet previous connection */
2728 if (check->cs) {
2729 cs_close(check->cs);
2730 retcode = -1; /* do not reuse the fd in the caller! */
2731 }
2732 eval_ret = tcpcheck_eval_connect(check, rule);
Christopher Faulet3cbdd222020-05-26 11:14:50 +02002733
2734 /* Refresh conn-stream and connection */
2735 cs = check->cs;
2736 conn = cs_conn(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002737 must_read = 1; last_read = 0;
2738 break;
2739 case TCPCHK_ACT_SEND:
2740 check->current_step = rule;
2741 eval_ret = tcpcheck_eval_send(check, rule);
2742 must_read = 1;
2743 break;
2744 case TCPCHK_ACT_EXPECT:
2745 check->current_step = rule;
2746 if (must_read) {
2747 if (check->proxy->timeout.check)
2748 check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
Willy Tarreauf3d34822014-12-08 12:11:28 +01002749
Christopher Faulet61cc8522020-04-20 14:54:42 +02002750 eval_ret = tcpcheck_eval_recv(check, rule);
2751 if (eval_ret == TCPCHK_EVAL_STOP)
2752 goto out_end_tcpcheck;
2753 else if (eval_ret == TCPCHK_EVAL_WAIT)
2754 goto out;
2755 last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS)));
2756 must_read = 0;
2757 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002758
Christopher Faulet61cc8522020-04-20 14:54:42 +02002759 eval_ret = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
2760 ? tcpcheck_eval_expect_http(check, rule, last_read)
2761 : tcpcheck_eval_expect(check, rule, last_read));
Willy Tarreau00149122017-10-04 18:05:01 +02002762
Christopher Faulet61cc8522020-04-20 14:54:42 +02002763 if (eval_ret == TCPCHK_EVAL_WAIT) {
2764 check->current_step = rule->expect.head;
Christopher Fauleta2fb0c32020-05-09 17:27:43 +02002765 if (!(check->wait_list.events & SUB_RETRY_RECV))
2766 conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002767 }
2768 break;
2769 case TCPCHK_ACT_ACTION_KW:
2770 /* Don't update the current step */
2771 eval_ret = tcpcheck_eval_action_kw(check, rule);
2772 break;
2773 default:
2774 /* Otherwise, just go to the next one and don't update
2775 * the current step
2776 */
2777 eval_ret = TCPCHK_EVAL_CONTINUE;
2778 break;
2779 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002780
Christopher Faulet61cc8522020-04-20 14:54:42 +02002781 switch (eval_ret) {
2782 case TCPCHK_EVAL_CONTINUE:
2783 break;
2784 case TCPCHK_EVAL_WAIT:
2785 goto out;
2786 case TCPCHK_EVAL_STOP:
2787 goto out_end_tcpcheck;
2788 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02002789 }
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002790
Christopher Faulet61cc8522020-04-20 14:54:42 +02002791 /* All rules was evaluated */
2792 if (check->current_step) {
2793 rule = check->current_step;
Willy Tarreau00149122017-10-04 18:05:01 +02002794
Christopher Faulet61cc8522020-04-20 14:54:42 +02002795 if (rule->action == TCPCHK_ACT_EXPECT) {
2796 struct buffer *msg;
Christopher Faulet1941bab2020-05-05 07:55:50 +02002797 enum healthcheck_status status;
Willy Tarreau00149122017-10-04 18:05:01 +02002798
Christopher Faulet61cc8522020-04-20 14:54:42 +02002799 if (check->server &&
2800 (check->server->proxy->options & PR_O_DISABLE404) &&
2801 (check->server->next_state != SRV_ST_STOPPED) &&
2802 (check->code == 404)) {
2803 set_server_check_status(check, HCHK_STATUS_L7OKCD, NULL);
2804 goto out_end_tcpcheck;
2805 }
Christopher Fauletd7e63962020-04-17 20:15:59 +02002806
Christopher Faulet61cc8522020-04-20 14:54:42 +02002807 msg = alloc_trash_chunk();
2808 if (msg)
Christopher Fauletb61caf42020-04-21 10:57:42 +02002809 tcpcheck_expect_onsuccess_message(msg, check, rule, IST_NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02002810 status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
2811 set_server_check_status(check, status, (msg ? b_head(msg) : "(tcp-check)"));
Christopher Faulet61cc8522020-04-20 14:54:42 +02002812 free_trash_chunk(msg);
2813 }
2814 else if (rule->action == TCPCHK_ACT_CONNECT) {
2815 const char *msg = ((rule->connect.options & TCPCHK_OPT_IMPLICIT) ? NULL : "(tcp-check)");
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002816 enum healthcheck_status status = HCHK_STATUS_L4OK;
2817#ifdef USE_OPENSSL
Christopher Fauletb0b8b262020-05-26 12:14:22 +02002818 if (ssl_sock_is_ssl(conn))
Christopher Fauletf73f5cc2020-04-27 12:06:55 +02002819 status = HCHK_STATUS_L6OK;
2820#endif
Christopher Faulet61cc8522020-04-20 14:54:42 +02002821 set_server_check_status(check, status, msg);
2822 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002823 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002824 else
2825 set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
Christopher Faulet14cd3162020-04-16 14:50:06 +02002826
Christopher Faulet61cc8522020-04-20 14:54:42 +02002827 out_end_tcpcheck:
2828 if ((conn && conn->flags & CO_FL_ERROR) || (cs && cs->flags & CS_FL_ERROR))
2829 chk_report_conn_err(check, errno, 0);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002830
Christopher Faulet61cc8522020-04-20 14:54:42 +02002831 out:
2832 return retcode;
2833}
Christopher Fauletbb591a12020-04-01 16:52:17 +02002834
Christopher Faulet14cd3162020-04-16 14:50:06 +02002835
Christopher Faulet61cc8522020-04-20 14:54:42 +02002836/**************************************************************************/
2837/************** Health-checks based on an external process ****************/
2838/**************************************************************************/
2839static struct list pid_list = LIST_HEAD_INIT(pid_list);
2840static struct pool_head *pool_head_pid_list;
2841__decl_spinlock(pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002842
Christopher Faulet61cc8522020-04-20 14:54:42 +02002843struct extcheck_env {
2844 char *name; /* environment variable name */
2845 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
2846};
Christopher Faulet14cd3162020-04-16 14:50:06 +02002847
Christopher Faulet61cc8522020-04-20 14:54:42 +02002848/* environment variables memory requirement for different types of data */
2849#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
2850 * such environment variables are not updatable. */
2851#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
2852#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
2853#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002854
Christopher Faulet61cc8522020-04-20 14:54:42 +02002855/* external checks environment variables */
2856enum {
2857 EXTCHK_PATH = 0,
Willy Tarreauca79f592019-07-17 19:04:47 +02002858
Christopher Faulet61cc8522020-04-20 14:54:42 +02002859 /* Proxy specific environment variables */
2860 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
2861 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
2862 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
2863 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Christopher Fauletbb591a12020-04-01 16:52:17 +02002864
Christopher Faulet61cc8522020-04-20 14:54:42 +02002865 /* Server specific environment variables */
2866 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
2867 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
2868 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
2869 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
2870 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
2871 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002872
Christopher Faulet61cc8522020-04-20 14:54:42 +02002873 EXTCHK_SIZE
2874};
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002875
Christopher Faulet61cc8522020-04-20 14:54:42 +02002876const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
2877 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
2878 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
2879 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
2880 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
2881 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
2882 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
2883 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
2884 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
2885 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
2886 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
2887 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
2888};
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002889
Christopher Faulet61cc8522020-04-20 14:54:42 +02002890void block_sigchld(void)
2891{
2892 sigset_t set;
2893 sigemptyset(&set);
2894 sigaddset(&set, SIGCHLD);
2895 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
2896}
Willy Tarreaube373152018-09-06 11:45:30 +02002897
Christopher Faulet61cc8522020-04-20 14:54:42 +02002898void unblock_sigchld(void)
2899{
2900 sigset_t set;
2901 sigemptyset(&set);
2902 sigaddset(&set, SIGCHLD);
2903 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002904}
Willy Tarreau2ab5c382019-07-17 18:48:07 +02002905
Christopher Faulet61cc8522020-04-20 14:54:42 +02002906static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02002907{
Christopher Faulet61cc8522020-04-20 14:54:42 +02002908 struct pid_list *elem;
2909 struct check *check = t->context;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002910
Christopher Faulet61cc8522020-04-20 14:54:42 +02002911 elem = pool_alloc(pool_head_pid_list);
2912 if (!elem)
2913 return NULL;
2914 elem->pid = pid;
2915 elem->t = t;
2916 elem->exited = 0;
2917 check->curpid = elem;
2918 LIST_INIT(&elem->list);
Gaetan Rivet08fdcb32020-02-28 11:04:21 +01002919
Christopher Faulet61cc8522020-04-20 14:54:42 +02002920 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2921 LIST_ADD(&pid_list, &elem->list);
2922 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002923
Christopher Faulet61cc8522020-04-20 14:54:42 +02002924 return elem;
2925}
Christopher Faulete5870d82020-04-15 11:32:03 +02002926
Christopher Faulet61cc8522020-04-20 14:54:42 +02002927static void pid_list_del(struct pid_list *elem)
2928{
2929 struct check *check;
Christopher Faulete5870d82020-04-15 11:32:03 +02002930
Christopher Faulet61cc8522020-04-20 14:54:42 +02002931 if (!elem)
2932 return;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002933
Christopher Faulet61cc8522020-04-20 14:54:42 +02002934 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2935 LIST_DEL(&elem->list);
2936 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002937
Christopher Faulet61cc8522020-04-20 14:54:42 +02002938 if (!elem->exited)
2939 kill(elem->pid, SIGTERM);
Christopher Faulet14cd3162020-04-16 14:50:06 +02002940
Christopher Faulet61cc8522020-04-20 14:54:42 +02002941 check = elem->t->context;
2942 check->curpid = NULL;
2943 pool_free(pool_head_pid_list, elem);
2944}
Christopher Faulet14cd3162020-04-16 14:50:06 +02002945
Christopher Faulet61cc8522020-04-20 14:54:42 +02002946/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
2947static void pid_list_expire(pid_t pid, int status)
2948{
2949 struct pid_list *elem;
Christopher Faulete5870d82020-04-15 11:32:03 +02002950
Christopher Faulet61cc8522020-04-20 14:54:42 +02002951 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
2952 list_for_each_entry(elem, &pid_list, list) {
2953 if (elem->pid == pid) {
2954 elem->t->expire = now_ms;
2955 elem->status = status;
2956 elem->exited = 1;
2957 task_wakeup(elem->t, TASK_WOKEN_IO);
2958 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02002959 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002960 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02002961 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
2962}
Christopher Fauleta202d1d2020-03-26 17:38:49 +01002963
Christopher Faulet61cc8522020-04-20 14:54:42 +02002964static void sigchld_handler(struct sig_handler *sh)
2965{
2966 pid_t pid;
2967 int status;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01002968
Christopher Faulet61cc8522020-04-20 14:54:42 +02002969 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
2970 pid_list_expire(pid, status);
2971}
Christopher Fauletf50f4e92020-03-30 19:52:29 +02002972
Christopher Faulet61cc8522020-04-20 14:54:42 +02002973static int init_pid_list(void)
2974{
2975 if (pool_head_pid_list != NULL)
2976 /* Nothing to do */
2977 return 0;
2978
2979 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
2980 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
2981 strerror(errno));
2982 return 1;
Christopher Faulet14cd3162020-04-16 14:50:06 +02002983 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02002984
Christopher Faulet61cc8522020-04-20 14:54:42 +02002985 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
2986 if (pool_head_pid_list == NULL) {
2987 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
2988 strerror(errno));
2989 return 1;
2990 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002991
Christopher Faulet61cc8522020-04-20 14:54:42 +02002992 return 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02002993}
2994
Christopher Faulet61cc8522020-04-20 14:54:42 +02002995/* helper macro to set an environment variable and jump to a specific label on failure. */
2996#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Christopher Fauletf9585d82020-04-16 13:25:58 +02002997
Christopher Faulet61cc8522020-04-20 14:54:42 +02002998/*
2999 * helper function to allocate enough memory to store an environment variable.
3000 * It will also check that the environment variable is updatable, and silently
3001 * fail if not.
3002 */
3003static int extchk_setenv(struct check *check, int idx, const char *value)
3004{
3005 int len, ret;
3006 char *envname;
3007 int vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003008
Christopher Faulet61cc8522020-04-20 14:54:42 +02003009 if (idx < 0 || idx >= EXTCHK_SIZE) {
3010 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
3011 return 1;
3012 }
Christopher Fauletf9585d82020-04-16 13:25:58 +02003013
Christopher Faulet61cc8522020-04-20 14:54:42 +02003014 envname = extcheck_envs[idx].name;
3015 vmaxlen = extcheck_envs[idx].vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003016
Christopher Faulet61cc8522020-04-20 14:54:42 +02003017 /* Check if the environment variable is already set, and silently reject
3018 * the update if this one is not updatable. */
3019 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
3020 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003021
Christopher Faulet61cc8522020-04-20 14:54:42 +02003022 /* Instead of sending NOT_USED, sending an empty value is preferable */
3023 if (strcmp(value, "NOT_USED") == 0) {
3024 value = "";
Christopher Fauletf9585d82020-04-16 13:25:58 +02003025 }
3026
Christopher Faulet61cc8522020-04-20 14:54:42 +02003027 len = strlen(envname) + 1;
3028 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
3029 len += strlen(value);
3030 else
3031 len += vmaxlen;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003032
Christopher Faulet61cc8522020-04-20 14:54:42 +02003033 if (!check->envp[idx])
3034 check->envp[idx] = malloc(len + 1);
Christopher Fauletf9585d82020-04-16 13:25:58 +02003035
Christopher Faulet61cc8522020-04-20 14:54:42 +02003036 if (!check->envp[idx]) {
3037 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
3038 return 1;
3039 }
3040 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
3041 if (ret < 0) {
3042 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
3043 return 1;
3044 }
3045 else if (ret > len) {
3046 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
3047 return 1;
3048 }
3049 return 0;
Christopher Fauletf9585d82020-04-16 13:25:58 +02003050}
3051
Christopher Faulet61cc8522020-04-20 14:54:42 +02003052static int prepare_external_check(struct check *check)
Christopher Faulete5870d82020-04-15 11:32:03 +02003053{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003054 struct server *s = check->server;
3055 struct proxy *px = s->proxy;
3056 struct listener *listener = NULL, *l;
3057 int i;
3058 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
3059 char buf[256];
Christopher Faulete5870d82020-04-15 11:32:03 +02003060
Christopher Faulet61cc8522020-04-20 14:54:42 +02003061 list_for_each_entry(l, &px->conf.listeners, by_fe)
3062 /* Use the first INET, INET6 or UNIX listener */
3063 if (l->addr.ss_family == AF_INET ||
3064 l->addr.ss_family == AF_INET6 ||
3065 l->addr.ss_family == AF_UNIX) {
3066 listener = l;
3067 break;
3068 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003069
Christopher Faulet61cc8522020-04-20 14:54:42 +02003070 check->curpid = NULL;
3071 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
3072 if (!check->envp) {
3073 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
3074 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003075 }
3076
Christopher Faulet61cc8522020-04-20 14:54:42 +02003077 check->argv = calloc(6, sizeof(char *));
3078 if (!check->argv) {
3079 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3080 goto err;
Christopher Faulet14cd3162020-04-16 14:50:06 +02003081 }
3082
Christopher Faulet61cc8522020-04-20 14:54:42 +02003083 check->argv[0] = px->check_command;
Christopher Faulete5870d82020-04-15 11:32:03 +02003084
Christopher Faulet61cc8522020-04-20 14:54:42 +02003085 if (!listener) {
3086 check->argv[1] = strdup("NOT_USED");
3087 check->argv[2] = strdup("NOT_USED");
Christopher Faulete5870d82020-04-15 11:32:03 +02003088 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003089 else if (listener->addr.ss_family == AF_INET ||
3090 listener->addr.ss_family == AF_INET6) {
3091 addr_to_str(&listener->addr, buf, sizeof(buf));
3092 check->argv[1] = strdup(buf);
3093 port_to_str(&listener->addr, buf, sizeof(buf));
3094 check->argv[2] = strdup(buf);
3095 }
3096 else if (listener->addr.ss_family == AF_UNIX) {
3097 const struct sockaddr_un *un;
Christopher Faulete5870d82020-04-15 11:32:03 +02003098
Christopher Faulet61cc8522020-04-20 14:54:42 +02003099 un = (struct sockaddr_un *)&listener->addr;
3100 check->argv[1] = strdup(un->sun_path);
3101 check->argv[2] = strdup("NOT_USED");
3102 }
3103 else {
3104 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
3105 goto err;
3106 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003107
Christopher Faulet61cc8522020-04-20 14:54:42 +02003108 if (!check->argv[1] || !check->argv[2]) {
3109 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3110 goto err;
3111 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003112
Christopher Faulet61cc8522020-04-20 14:54:42 +02003113 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
3114 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
3115 if (!check->argv[3] || !check->argv[4]) {
3116 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3117 goto err;
3118 }
Christopher Faulet14cd3162020-04-16 14:50:06 +02003119
Christopher Faulet61cc8522020-04-20 14:54:42 +02003120 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3121 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3122 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulete5870d82020-04-15 11:32:03 +02003123
Christopher Faulet61cc8522020-04-20 14:54:42 +02003124 for (i = 0; i < 5; i++) {
3125 if (!check->argv[i]) {
3126 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
3127 goto err;
Christopher Faulete5870d82020-04-15 11:32:03 +02003128 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003129 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003130
Christopher Faulet61cc8522020-04-20 14:54:42 +02003131 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
3132 /* Add proxy environment variables */
3133 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
3134 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
3135 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
3136 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
3137 /* Add server environment variables */
3138 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
3139 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
3140 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
3141 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
3142 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
3143 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003144
Christopher Faulet61cc8522020-04-20 14:54:42 +02003145 /* Ensure that we don't leave any hole in check->envp */
3146 for (i = 0; i < EXTCHK_SIZE; i++)
3147 if (!check->envp[i])
3148 EXTCHK_SETENV(check, i, "", err);
Christopher Faulete5870d82020-04-15 11:32:03 +02003149
Christopher Faulet61cc8522020-04-20 14:54:42 +02003150 return 1;
3151err:
3152 if (check->envp) {
3153 for (i = 0; i < EXTCHK_SIZE; i++)
3154 free(check->envp[i]);
3155 free(check->envp);
3156 check->envp = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003157 }
3158
Christopher Faulet61cc8522020-04-20 14:54:42 +02003159 if (check->argv) {
3160 for (i = 1; i < 5; i++)
3161 free(check->argv[i]);
3162 free(check->argv);
3163 check->argv = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02003164 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003165 return 0;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003166}
Gaetan Rivetf8ba6772020-02-07 15:37:17 +01003167
Christopher Faulet61cc8522020-04-20 14:54:42 +02003168/*
3169 * establish a server health-check that makes use of a process.
3170 *
3171 * It can return one of :
3172 * - SF_ERR_NONE if everything's OK
3173 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
3174 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
3175 *
3176 * Blocks and then unblocks SIGCHLD
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003177 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003178static int connect_proc_chk(struct task *t)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003179{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003180 char buf[256];
3181 struct check *check = t->context;
3182 struct server *s = check->server;
3183 struct proxy *px = s->proxy;
3184 int status;
3185 pid_t pid;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003186
Christopher Faulet61cc8522020-04-20 14:54:42 +02003187 status = SF_ERR_RESOURCE;
Willy Tarreauf2c87352015-05-13 12:08:21 +02003188
Christopher Faulet61cc8522020-04-20 14:54:42 +02003189 block_sigchld();
Willy Tarreau7df8ca62019-07-15 10:57:51 +02003190
Christopher Faulet61cc8522020-04-20 14:54:42 +02003191 pid = fork();
3192 if (pid < 0) {
3193 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
3194 (global.tune.options & GTUNE_INSECURE_FORK) ?
3195 "" : " (likely caused by missing 'insecure-fork-wanted')",
3196 strerror(errno));
3197 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003198 goto out;
3199 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003200 if (pid == 0) {
3201 /* Child */
3202 extern char **environ;
3203 struct rlimit limit;
3204 int fd;
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003205
Christopher Faulet61cc8522020-04-20 14:54:42 +02003206 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
3207 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Baptiste Assmann69e273f2013-12-11 00:52:19 +01003208
Christopher Faulet61cc8522020-04-20 14:54:42 +02003209 my_closefrom(fd);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003210
Christopher Faulet61cc8522020-04-20 14:54:42 +02003211 /* restore the initial FD limits */
3212 limit.rlim_cur = rlim_fd_cur_at_boot;
3213 limit.rlim_max = rlim_fd_max_at_boot;
3214 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
3215 getrlimit(RLIMIT_NOFILE, &limit);
3216 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
3217 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
3218 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
3219 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003220
Christopher Faulet61cc8522020-04-20 14:54:42 +02003221 environ = check->envp;
Willy Tarreaufbe0edf2013-12-06 16:54:31 +01003222
Christopher Faulet61cc8522020-04-20 14:54:42 +02003223 /* Update some environment variables and command args: curconn, server addr and server port */
3224 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003225
Christopher Faulet61cc8522020-04-20 14:54:42 +02003226 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
3227 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003228
Christopher Faulet61cc8522020-04-20 14:54:42 +02003229 *check->argv[4] = 0;
3230 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
3231 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
3232 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003233
Christopher Faulet61cc8522020-04-20 14:54:42 +02003234 haproxy_unblock_signals();
3235 execvp(px->check_command, check->argv);
3236 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
3237 strerror(errno));
3238 exit(-1);
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003239 }
3240
Christopher Faulet61cc8522020-04-20 14:54:42 +02003241 /* Parent */
3242 if (check->result == CHK_RES_UNKNOWN) {
3243 if (pid_list_add(pid, t) != NULL) {
3244 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3245
3246 if (px->timeout.check && px->timeout.connect) {
3247 int t_con = tick_add(now_ms, px->timeout.connect);
3248 t->expire = tick_first(t->expire, t_con);
3249 }
3250 status = SF_ERR_NONE;
3251 goto out;
3252 }
3253 else {
3254 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3255 }
3256 kill(pid, SIGTERM); /* process creation error */
3257 }
3258 else
3259 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
3260
3261out:
3262 unblock_sigchld();
3263 return status;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003264}
3265
Christopher Faulet61cc8522020-04-20 14:54:42 +02003266/*
3267 * manages a server health-check that uses an external process. Returns
3268 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003269 *
3270 * Please do NOT place any return statement in this function and only leave
Christopher Faulet61cc8522020-04-20 14:54:42 +02003271 * via the out_unlock label.
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003272 */
Christopher Faulet61cc8522020-04-20 14:54:42 +02003273static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003274{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003275 struct check *check = context;
3276 struct server *s = check->server;
3277 int rv;
3278 int ret;
3279 int expired = tick_is_expired(t->expire, now_ms);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003280
Christopher Faulet61cc8522020-04-20 14:54:42 +02003281 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3282 if (!(check->state & CHK_ST_INPROGRESS)) {
3283 /* no check currently running */
3284 if (!expired) /* woke up too early */
3285 goto out_unlock;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003286
Christopher Faulet61cc8522020-04-20 14:54:42 +02003287 /* we don't send any health-checks when the proxy is
3288 * stopped, the server should not be checked or the check
3289 * is disabled.
3290 */
3291 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3292 s->proxy->state == PR_STSTOPPED)
3293 goto reschedule;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003294
Christopher Faulet61cc8522020-04-20 14:54:42 +02003295 /* we'll initiate a new check */
3296 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003297
Christopher Faulet61cc8522020-04-20 14:54:42 +02003298 check->state |= CHK_ST_INPROGRESS;
3299
3300 ret = connect_proc_chk(t);
3301 if (ret == SF_ERR_NONE) {
3302 /* the process was forked, we allow up to min(inter,
3303 * timeout.connect) for it to report its status, but
3304 * only when timeout.check is set as it may be to short
3305 * for a full check otherwise.
3306 */
3307 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
3308
3309 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
3310 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
3311 t->expire = tick_first(t->expire, t_con);
Christopher Faulet370e0f12020-04-16 09:52:42 +02003312 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003313 task_set_affinity(t, tid_bit);
3314 goto reschedule;
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +02003315 }
Gaetan Rivetefab6c62020-02-07 15:37:17 +01003316
Christopher Faulet61cc8522020-04-20 14:54:42 +02003317 /* here, we failed to start the check */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003318
Christopher Faulet61cc8522020-04-20 14:54:42 +02003319 check->state &= ~CHK_ST_INPROGRESS;
3320 check_notify_failure(check);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003321
Christopher Faulet61cc8522020-04-20 14:54:42 +02003322 /* we allow up to min(inter, timeout.connect) for a connection
3323 * to establish but only when timeout.check is set
3324 * as it may be to short for a full check otherwise
3325 */
3326 while (tick_is_expired(t->expire, now_ms)) {
3327 int t_con;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003328
Christopher Faulet61cc8522020-04-20 14:54:42 +02003329 t_con = tick_add(t->expire, s->proxy->timeout.connect);
3330 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003331
Christopher Faulet61cc8522020-04-20 14:54:42 +02003332 if (s->proxy->timeout.check)
3333 t->expire = tick_first(t->expire, t_con);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003334 }
Gaetan Rivet05d692d2020-02-14 17:42:54 +01003335 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003336 else {
3337 /* there was a test running.
3338 * First, let's check whether there was an uncaught error,
3339 * which can happen on connect timeout or error.
3340 */
3341 if (check->result == CHK_RES_UNKNOWN) {
3342 /* good connection is enough for pure TCP check */
3343 struct pid_list *elem = check->curpid;
3344 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet370e0f12020-04-16 09:52:42 +02003345
Christopher Faulet61cc8522020-04-20 14:54:42 +02003346 if (elem->exited) {
3347 status = elem->status; /* Save in case the process exits between use below */
3348 if (!WIFEXITED(status))
3349 check->code = -1;
3350 else
3351 check->code = WEXITSTATUS(status);
3352 if (!WIFEXITED(status) || WEXITSTATUS(status))
3353 status = HCHK_STATUS_PROCERR;
3354 else
3355 status = HCHK_STATUS_PROCOK;
3356 } else if (expired) {
3357 status = HCHK_STATUS_PROCTOUT;
3358 ha_warning("kill %d\n", (int)elem->pid);
3359 kill(elem->pid, SIGTERM);
3360 }
3361 set_server_check_status(check, status, NULL);
3362 }
Willy Tarreauf2c87352015-05-13 12:08:21 +02003363
Christopher Faulet61cc8522020-04-20 14:54:42 +02003364 if (check->result == CHK_RES_FAILED) {
3365 /* a failure or timeout detected */
3366 check_notify_failure(check);
3367 }
3368 else if (check->result == CHK_RES_CONDPASS) {
3369 /* check is OK but asks for stopping mode */
3370 check_notify_stopping(check);
3371 }
3372 else if (check->result == CHK_RES_PASSED) {
3373 /* a success was detected */
3374 check_notify_success(check);
3375 }
3376 task_set_affinity(t, 1);
3377 check->state &= ~CHK_ST_INPROGRESS;
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003378
Christopher Faulet61cc8522020-04-20 14:54:42 +02003379 pid_list_del(check->curpid);
Baptiste Assmann22b09d22015-05-01 08:03:04 +02003380
Christopher Faulet61cc8522020-04-20 14:54:42 +02003381 rv = 0;
3382 if (global.spread_checks > 0) {
3383 rv = srv_getinter(check) * global.spread_checks / 100;
3384 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3385 }
3386 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
3387 }
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +01003388
Christopher Faulet61cc8522020-04-20 14:54:42 +02003389 reschedule:
3390 while (tick_is_expired(t->expire, now_ms))
3391 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulete5870d82020-04-15 11:32:03 +02003392
Christopher Faulet61cc8522020-04-20 14:54:42 +02003393 out_unlock:
3394 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3395 return t;
3396}
Baptiste Assmann248f1172018-03-01 21:49:01 +01003397
Baptiste Assmann248f1172018-03-01 21:49:01 +01003398
Christopher Faulet61cc8522020-04-20 14:54:42 +02003399/**************************************************************************/
3400/***************** Health-checks based on connections *********************/
3401/**************************************************************************/
3402/* This function is used only for server health-checks. It handles connection
3403 * status updates including errors. If necessary, it wakes the check task up.
3404 * It returns 0 on normal cases, <0 if at least one close() has happened on the
3405 * connection (eg: reconnect). It relies on tcpcheck_main().
3406 */
3407static int wake_srv_chk(struct conn_stream *cs)
3408{
3409 struct connection *conn = cs->conn;
3410 struct check *check = cs->data;
3411 struct email_alertq *q = container_of(check, typeof(*q), check);
3412 int ret = 0;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003413
Christopher Faulet61cc8522020-04-20 14:54:42 +02003414 if (check->server)
3415 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3416 else
3417 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulete5870d82020-04-15 11:32:03 +02003418
Christopher Faulet61cc8522020-04-20 14:54:42 +02003419 /* we may have to make progress on the TCP checks */
3420 ret = tcpcheck_main(check);
Christopher Fauletdf38f882020-04-07 16:04:38 +02003421
Christopher Faulet61cc8522020-04-20 14:54:42 +02003422 cs = check->cs;
3423 conn = cs->conn;
Christopher Fauletdf38f882020-04-07 16:04:38 +02003424
Christopher Faulet61cc8522020-04-20 14:54:42 +02003425 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
3426 /* We may get error reports bypassing the I/O handlers, typically
3427 * the case when sending a pure TCP check which fails, then the I/O
3428 * handlers above are not called. This is completely handled by the
3429 * main processing task so let's simply wake it up. If we get here,
3430 * we expect errno to still be valid.
3431 */
3432 chk_report_conn_err(check, errno, 0);
3433 task_wakeup(check->task, TASK_WOKEN_IO);
3434 }
3435
3436 if (check->result != CHK_RES_UNKNOWN) {
3437 /* Check complete or aborted. If connection not yet closed do it
3438 * now and wake the check task up to be sure the result is
3439 * handled ASAP. */
3440 conn_sock_drain(conn);
3441 cs_close(cs);
3442 ret = -1;
3443 /* We may have been scheduled to run, and the
3444 * I/O handler expects to have a cs, so remove
3445 * the tasklet
3446 */
3447 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3448 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02003449 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003450
3451 if (check->server)
3452 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
Christopher Fauletec07e382020-04-07 14:56:26 +02003453 else
Christopher Faulet61cc8522020-04-20 14:54:42 +02003454 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003455
Christopher Faulet61cc8522020-04-20 14:54:42 +02003456 /* if a connection got replaced, we must absolutely prevent the connection
3457 * handler from touching its fd, and perform the FD polling updates ourselves
3458 */
3459 if (ret < 0)
3460 conn_cond_update_polling(conn);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003461
Christopher Faulet61cc8522020-04-20 14:54:42 +02003462 return ret;
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02003463}
3464
Christopher Faulet61cc8522020-04-20 14:54:42 +02003465/* This function checks if any I/O is wanted, and if so, attempts to do so */
3466static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Simon Hormanb1900d52015-01-30 11:22:54 +09003467{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003468 struct check *check = ctx;
3469 struct conn_stream *cs = check->cs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003470
Christopher Faulet3d5e1212020-05-28 14:34:02 +02003471 wake_srv_chk(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003472 return NULL;
Simon Hormanbfb5d332015-01-30 11:22:55 +09003473}
3474
Christopher Faulet61cc8522020-04-20 14:54:42 +02003475/* manages a server health-check that uses a connection. Returns
3476 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
3477 *
3478 * Please do NOT place any return statement in this function and only leave
3479 * via the out_unlock label.
3480 */
3481static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003482{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003483 struct check *check = context;
3484 struct proxy *proxy = check->proxy;
3485 struct conn_stream *cs = check->cs;
3486 struct connection *conn = cs_conn(cs);
3487 int rv;
3488 int expired = tick_is_expired(t->expire, now_ms);
Christopher Fauletf50f4e92020-03-30 19:52:29 +02003489
Christopher Faulet61cc8522020-04-20 14:54:42 +02003490 if (check->server)
3491 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
3492 if (!(check->state & CHK_ST_INPROGRESS)) {
3493 /* no check currently running */
3494 if (!expired) /* woke up too early */
3495 goto out_unlock;
Christopher Faulete5870d82020-04-15 11:32:03 +02003496
Christopher Faulet61cc8522020-04-20 14:54:42 +02003497 /* we don't send any health-checks when the proxy is
3498 * stopped, the server should not be checked or the check
3499 * is disabled.
3500 */
3501 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
3502 proxy->state == PR_STSTOPPED)
3503 goto reschedule;
Christopher Faulete5870d82020-04-15 11:32:03 +02003504
Christopher Faulet61cc8522020-04-20 14:54:42 +02003505 /* we'll initiate a new check */
3506 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulete5870d82020-04-15 11:32:03 +02003507
Christopher Faulet61cc8522020-04-20 14:54:42 +02003508 check->state |= CHK_ST_INPROGRESS;
3509 b_reset(&check->bi);
3510 b_reset(&check->bo);
Christopher Faulete5870d82020-04-15 11:32:03 +02003511
Christopher Faulet61cc8522020-04-20 14:54:42 +02003512 task_set_affinity(t, tid_bit);
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003513
Christopher Faulet99ff1052020-05-25 07:32:01 +02003514 check->current_step = NULL;
3515 tcpcheck_main(check);
3516 goto out_unlock;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003517 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003518 else {
3519 /* there was a test running.
3520 * First, let's check whether there was an uncaught error,
3521 * which can happen on connect timeout or error.
3522 */
3523 if (check->result == CHK_RES_UNKNOWN) {
3524 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
3525 chk_report_conn_err(check, 0, expired);
3526 }
3527 else
3528 goto out_unlock; /* timeout not reached, wait again */
3529 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003530
Christopher Faulet61cc8522020-04-20 14:54:42 +02003531 /* check complete or aborted */
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003532
Christopher Faulet61cc8522020-04-20 14:54:42 +02003533 check->current_step = NULL;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003534
Christopher Faulet61cc8522020-04-20 14:54:42 +02003535 if (conn && conn->xprt) {
3536 /* The check was aborted and the connection was not yet closed.
3537 * This can happen upon timeout, or when an external event such
3538 * as a failed response coupled with "observe layer7" caused the
3539 * server state to be suddenly changed.
3540 */
3541 conn_sock_drain(conn);
3542 cs_close(cs);
3543 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003544
Christopher Faulet61cc8522020-04-20 14:54:42 +02003545 if (cs) {
3546 if (check->wait_list.events)
Christopher Faulet0b9376a2020-04-24 16:20:49 +02003547 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003548 /* We may have been scheduled to run, and the
3549 * I/O handler expects to have a cs, so remove
3550 * the tasklet
3551 */
3552 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
3553 cs_destroy(cs);
3554 cs = check->cs = NULL;
3555 conn = NULL;
3556 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003557
Christopher Fauletb693a0d2020-04-27 15:59:22 +02003558 if (check->sess != NULL) {
3559 vars_prune(&check->vars, check->sess, NULL);
3560 session_free(check->sess);
3561 check->sess = NULL;
3562 }
3563
Christopher Faulet61cc8522020-04-20 14:54:42 +02003564 if (check->server) {
3565 if (check->result == CHK_RES_FAILED) {
3566 /* a failure or timeout detected */
3567 check_notify_failure(check);
3568 }
3569 else if (check->result == CHK_RES_CONDPASS) {
3570 /* check is OK but asks for stopping mode */
3571 check_notify_stopping(check);
3572 }
3573 else if (check->result == CHK_RES_PASSED) {
3574 /* a success was detected */
3575 check_notify_success(check);
3576 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003577 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003578 task_set_affinity(t, MAX_THREADS_MASK);
3579 check->state &= ~CHK_ST_INPROGRESS;
3580
3581 if (check->server) {
3582 rv = 0;
3583 if (global.spread_checks > 0) {
3584 rv = srv_getinter(check) * global.spread_checks / 100;
3585 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
3586 }
3587 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003588 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003589 }
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003590
Christopher Faulet61cc8522020-04-20 14:54:42 +02003591 reschedule:
3592 while (tick_is_expired(t->expire, now_ms))
3593 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
3594 out_unlock:
3595 if (check->server)
3596 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
3597 return t;
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003598}
3599
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003600
Christopher Faulet61cc8522020-04-20 14:54:42 +02003601/**************************************************************************/
3602/******************* Internals to parse tcp-check rules *******************/
3603/**************************************************************************/
3604struct action_kw_list tcp_check_keywords = {
3605 .list = LIST_HEAD_INIT(tcp_check_keywords.list),
3606};
Christopher Faulet7a1e2e12020-04-02 18:05:11 +02003607
Christopher Faulet61cc8522020-04-20 14:54:42 +02003608/* Return the struct action_kw associated to a keyword */
3609static struct action_kw *action_kw_tcp_check_lookup(const char *kw)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003610{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003611 return action_lookup(&tcp_check_keywords.list, kw);
3612}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003613
Christopher Faulet61cc8522020-04-20 14:54:42 +02003614static void action_kw_tcp_check_build_list(struct buffer *chk)
3615{
3616 action_build_list(&tcp_check_keywords.list, chk);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003617}
3618
Christopher Faulet61cc8522020-04-20 14:54:42 +02003619/* Creates a tcp-check rule resulting from parsing a custom keyword. NULL is
3620 * returned on error.
3621 */
3622static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
3623 struct list *rules, struct action_kw *kw,
3624 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003625{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003626 struct tcpcheck_rule *chk = NULL;
3627 struct act_rule *actrule = NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003628
Christopher Faulet61cc8522020-04-20 14:54:42 +02003629 actrule = calloc(1, sizeof(*actrule));
3630 if (!actrule) {
3631 memprintf(errmsg, "out of memory");
3632 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003633 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003634 actrule->kw = kw;
3635 actrule->from = ACT_F_TCP_CHK;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003636
Christopher Faulet61cc8522020-04-20 14:54:42 +02003637 cur_arg++;
3638 if (kw->parse((const char **)args, &cur_arg, px, actrule, errmsg) == ACT_RET_PRS_ERR) {
3639 memprintf(errmsg, "'%s' : %s", kw->kw, *errmsg);
3640 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003641 }
3642
Christopher Faulet61cc8522020-04-20 14:54:42 +02003643 chk = calloc(1, sizeof(*chk));
3644 if (!chk) {
3645 memprintf(errmsg, "out of memory");
3646 goto error;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003647 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003648 chk->action = TCPCHK_ACT_ACTION_KW;
3649 chk->action_kw.rule = actrule;
3650 return chk;
Christopher Faulet0108bb32017-10-20 21:34:32 +02003651
3652 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003653 free(actrule);
3654 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003655}
3656
Christopher Faulet61cc8522020-04-20 14:54:42 +02003657/* Parses and creates a tcp-check connect or an http-check connect rule. NULL is
3658 * returned on error.
3659 */
3660static struct tcpcheck_rule *parse_tcpcheck_connect(char **args, int cur_arg, struct proxy *px, struct list *rules,
3661 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003662{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003663 struct tcpcheck_rule *chk = NULL;
3664 struct sockaddr_storage *sk = NULL;
3665 char *comment = NULL, *sni = NULL, *alpn = NULL;
3666 struct sample_expr *port_expr = NULL;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003667 const struct mux_proto_list *mux_proto = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003668 unsigned short conn_opts = 0;
3669 long port = 0;
3670 int alpn_len = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003671
Christopher Faulet61cc8522020-04-20 14:54:42 +02003672 list_for_each_entry(chk, rules, list) {
3673 if (chk->action == TCPCHK_ACT_CONNECT)
3674 break;
3675 if (chk->action == TCPCHK_ACT_COMMENT ||
3676 chk->action == TCPCHK_ACT_ACTION_KW ||
3677 (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
3678 continue;
Gaetan Rivetb616add2020-02-07 15:37:17 +01003679
Christopher Faulet61cc8522020-04-20 14:54:42 +02003680 memprintf(errmsg, "first step MUST also be a 'connect', "
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05003681 "optionally preceded by a 'set-var', an 'unset-var' or a 'comment', "
Christopher Faulet61cc8522020-04-20 14:54:42 +02003682 "when there is a 'connect' step in the tcp-check ruleset");
3683 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003684 }
3685
Christopher Faulet61cc8522020-04-20 14:54:42 +02003686 cur_arg++;
3687 while (*(args[cur_arg])) {
3688 if (strcmp(args[cur_arg], "default") == 0)
3689 conn_opts |= TCPCHK_OPT_DEFAULT_CONNECT;
3690 else if (strcmp(args[cur_arg], "addr") == 0) {
3691 int port1, port2;
3692 struct protocol *proto;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003693
Christopher Faulet61cc8522020-04-20 14:54:42 +02003694 if (!*(args[cur_arg+1])) {
3695 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[cur_arg]);
3696 goto error;
3697 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003698
Christopher Faulet61cc8522020-04-20 14:54:42 +02003699 sk = str2sa_range(args[cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3700 if (!sk) {
3701 memprintf(errmsg, "'%s' : %s.", args[cur_arg], *errmsg);
3702 goto error;
3703 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003704
Christopher Faulet61cc8522020-04-20 14:54:42 +02003705 proto = protocol_by_family(sk->ss_family);
3706 if (!proto || !proto->connect) {
3707 memprintf(errmsg, "'%s' : connect() not supported for this address family.\n",
3708 args[cur_arg]);
3709 goto error;
3710 }
Gaetan Rivet48219dc2020-02-21 18:41:28 +01003711
Christopher Faulet61cc8522020-04-20 14:54:42 +02003712 if (port1 != port2) {
3713 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'\n",
3714 args[cur_arg], args[cur_arg+1]);
3715 goto error;
3716 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003717
Christopher Faulet61cc8522020-04-20 14:54:42 +02003718 cur_arg++;
3719 }
3720 else if (strcmp(args[cur_arg], "port") == 0) {
3721 const char *p, *end;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003722
Christopher Faulet61cc8522020-04-20 14:54:42 +02003723 if (!*(args[cur_arg+1])) {
3724 memprintf(errmsg, "'%s' expects a port number or a sample expression as argument.", args[cur_arg]);
3725 goto error;
3726 }
3727 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003728
Christopher Faulet61cc8522020-04-20 14:54:42 +02003729 port = 0;
3730 release_sample_expr(port_expr);
3731 p = args[cur_arg]; end = p + strlen(p);
3732 port = read_uint(&p, end);
3733 if (p != end) {
3734 int idx = 0;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003735
Christopher Faulet61cc8522020-04-20 14:54:42 +02003736 px->conf.args.ctx = ARGC_SRV;
3737 port_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
3738 file, line, errmsg, &px->conf.args, NULL);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003739
Christopher Faulet61cc8522020-04-20 14:54:42 +02003740 if (!port_expr) {
3741 memprintf(errmsg, "error detected while parsing port expression : %s", *errmsg);
3742 goto error;
3743 }
3744 if (!(port_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
3745 memprintf(errmsg, "error detected while parsing port expression : "
3746 " fetch method '%s' extracts information from '%s', "
3747 "none of which is available here.\n",
3748 args[cur_arg], sample_src_names(port_expr->fetch->use));
3749 goto error;
3750 }
3751 px->http_needed |= !!(port_expr->fetch->use & SMP_USE_HTTP_ANY);
3752 }
3753 else if (port > 65535 || port < 1) {
3754 memprintf(errmsg, "expects a valid TCP port (from range 1 to 65535) or a sample expression, got %s.",
3755 args[cur_arg]);
3756 goto error;
3757 }
3758 }
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003759 else if (strcmp(args[cur_arg], "proto") == 0) {
3760 if (!*(args[cur_arg+1])) {
3761 memprintf(errmsg, "'%s' expects a MUX protocol as argument.", args[cur_arg]);
3762 goto error;
3763 }
3764 mux_proto = get_mux_proto(ist2(args[cur_arg+1], strlen(args[cur_arg+1])));
3765 if (!mux_proto) {
3766 memprintf(errmsg, "'%s' : unknown MUX protocol '%s'.", args[cur_arg], args[cur_arg+1]);
3767 goto error;
3768 }
3769 cur_arg++;
3770 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003771 else if (strcmp(args[cur_arg], "comment") == 0) {
3772 if (!*(args[cur_arg+1])) {
3773 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3774 goto error;
3775 }
3776 cur_arg++;
3777 free(comment);
3778 comment = strdup(args[cur_arg]);
3779 if (!comment) {
3780 memprintf(errmsg, "out of memory");
3781 goto error;
3782 }
3783 }
3784 else if (strcmp(args[cur_arg], "send-proxy") == 0)
3785 conn_opts |= TCPCHK_OPT_SEND_PROXY;
3786 else if (strcmp(args[cur_arg], "via-socks4") == 0)
3787 conn_opts |= TCPCHK_OPT_SOCKS4;
3788 else if (strcmp(args[cur_arg], "linger") == 0)
3789 conn_opts |= TCPCHK_OPT_LINGER;
3790#ifdef USE_OPENSSL
3791 else if (strcmp(args[cur_arg], "ssl") == 0) {
3792 px->options |= PR_O_TCPCHK_SSL;
3793 conn_opts |= TCPCHK_OPT_SSL;
3794 }
3795 else if (strcmp(args[cur_arg], "sni") == 0) {
3796 if (!*(args[cur_arg+1])) {
3797 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3798 goto error;
3799 }
3800 cur_arg++;
3801 free(sni);
3802 sni = strdup(args[cur_arg]);
3803 if (!sni) {
3804 memprintf(errmsg, "out of memory");
3805 goto error;
3806 }
3807 }
3808 else if (strcmp(args[cur_arg], "alpn") == 0) {
3809#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
3810 free(alpn);
3811 if (ssl_sock_parse_alpn(args[cur_arg + 1], &alpn, &alpn_len, errmsg)) {
3812 memprintf(errmsg, "'%s' : %s", args[cur_arg], *errmsg);
3813 goto error;
3814 }
3815 cur_arg++;
3816#else
3817 memprintf(errmsg, "'%s' : library does not support TLS ALPN extension.", args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003818 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003819#endif
3820 }
3821#endif /* USE_OPENSSL */
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003822
Christopher Faulet61cc8522020-04-20 14:54:42 +02003823 else {
3824 memprintf(errmsg, "expects 'comment', 'port', 'addr', 'send-proxy'"
3825#ifdef USE_OPENSSL
3826 ", 'ssl', 'sni', 'alpn'"
3827#endif /* USE_OPENSSL */
3828 " or 'via-socks4', 'linger', 'default' but got '%s' as argument.",
3829 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003830 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003831 }
3832 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003833 }
3834
Christopher Faulet61cc8522020-04-20 14:54:42 +02003835 chk = calloc(1, sizeof(*chk));
3836 if (!chk) {
3837 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003838 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003839 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003840 chk->action = TCPCHK_ACT_CONNECT;
3841 chk->comment = comment;
3842 chk->connect.port = port;
3843 chk->connect.options = conn_opts;
3844 chk->connect.sni = sni;
3845 chk->connect.alpn = alpn;
3846 chk->connect.alpn_len= alpn_len;
3847 chk->connect.port_expr= port_expr;
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003848 chk->connect.mux_proto= mux_proto;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003849 if (sk)
3850 chk->connect.addr = *sk;
3851 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003852
Christopher Faulet61cc8522020-04-20 14:54:42 +02003853 error:
3854 free(alpn);
3855 free(sni);
3856 free(comment);
3857 release_sample_expr(port_expr);
3858 return NULL;
3859}
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003860
Christopher Faulet61cc8522020-04-20 14:54:42 +02003861/* Parses and creates a tcp-check send rule. NULL is returned on error */
3862static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
3863 const char *file, int line, char **errmsg)
3864{
3865 struct tcpcheck_rule *chk = NULL;
3866 char *comment = NULL, *data = NULL;
3867 enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003868
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003869 if (strcmp(args[cur_arg], "send-binary-lf") == 0)
3870 type = TCPCHK_SEND_BINARY_LF;
3871 else if (strcmp(args[cur_arg], "send-binary") == 0)
3872 type = TCPCHK_SEND_BINARY;
3873 else if (strcmp(args[cur_arg], "send-lf") == 0)
3874 type = TCPCHK_SEND_STRING_LF;
3875 else if (strcmp(args[cur_arg], "send") == 0)
3876 type = TCPCHK_SEND_STRING;
3877
Christopher Faulet61cc8522020-04-20 14:54:42 +02003878 if (!*(args[cur_arg+1])) {
3879 memprintf(errmsg, "'%s' expects a %s as argument",
3880 (type == TCPCHK_SEND_BINARY ? "binary string": "string"), args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003881 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003882 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003883
Christopher Faulet61cc8522020-04-20 14:54:42 +02003884 data = args[cur_arg+1];
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003885
Christopher Faulet61cc8522020-04-20 14:54:42 +02003886 cur_arg += 2;
3887 while (*(args[cur_arg])) {
3888 if (strcmp(args[cur_arg], "comment") == 0) {
3889 if (!*(args[cur_arg+1])) {
3890 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3891 goto error;
3892 }
3893 cur_arg++;
3894 free(comment);
3895 comment = strdup(args[cur_arg]);
3896 if (!comment) {
3897 memprintf(errmsg, "out of memory");
3898 goto error;
3899 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003900 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003901 else {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003902 memprintf(errmsg, "expects 'comment' but got '%s' as argument.",
Christopher Faulet61cc8522020-04-20 14:54:42 +02003903 args[cur_arg]);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003904 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003905 }
3906 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003907 }
3908
Christopher Faulet61cc8522020-04-20 14:54:42 +02003909 chk = calloc(1, sizeof(*chk));
3910 if (!chk) {
3911 memprintf(errmsg, "out of memory");
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003912 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003913 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003914 chk->action = TCPCHK_ACT_SEND;
3915 chk->comment = comment;
3916 chk->send.type = type;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003917
Christopher Faulet61cc8522020-04-20 14:54:42 +02003918 switch (chk->send.type) {
3919 case TCPCHK_SEND_STRING:
3920 chk->send.data = ist2(strdup(data), strlen(data));
3921 if (!isttest(chk->send.data)) {
3922 memprintf(errmsg, "out of memory");
3923 goto error;
3924 }
3925 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003926 case TCPCHK_SEND_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02003927 int len = chk->send.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003928 if (parse_binary(data, &chk->send.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003929 memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
3930 goto error;
3931 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003932 chk->send.data.len = len;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003933 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02003934 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02003935 case TCPCHK_SEND_STRING_LF:
3936 case TCPCHK_SEND_BINARY_LF:
3937 LIST_INIT(&chk->send.fmt);
3938 px->conf.args.ctx = ARGC_SRV;
3939 if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
3940 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
3941 goto error;
3942 }
3943 break;
3944 case TCPCHK_SEND_HTTP:
3945 case TCPCHK_SEND_UNDEF:
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003946 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003947 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003948
Christopher Faulet61cc8522020-04-20 14:54:42 +02003949 return chk;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003950
Christopher Faulet61cc8522020-04-20 14:54:42 +02003951 error:
3952 free(chk);
3953 free(comment);
3954 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003955}
3956
Christopher Faulet61cc8522020-04-20 14:54:42 +02003957/* Parses and creates a http-check send rule. NULL is returned on error */
3958static struct tcpcheck_rule *parse_tcpcheck_send_http(char **args, int cur_arg, struct proxy *px, struct list *rules,
3959 const char *file, int line, char **errmsg)
Simon Horman0ba0e4a2015-01-30 11:23:00 +09003960{
Christopher Faulet61cc8522020-04-20 14:54:42 +02003961 struct tcpcheck_rule *chk = NULL;
3962 struct tcpcheck_http_hdr *hdr = NULL;
3963 struct http_hdr hdrs[global.tune.max_http_hdr];
3964 char *meth = NULL, *uri = NULL, *vsn = NULL;
3965 char *body = NULL, *comment = NULL;
3966 unsigned int flags = 0;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02003967 int i = 0, host_hdr = -1;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003968
3969 cur_arg++;
3970 while (*(args[cur_arg])) {
3971 if (strcmp(args[cur_arg], "meth") == 0) {
3972 if (!*(args[cur_arg+1])) {
3973 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3974 goto error;
3975 }
3976 cur_arg++;
3977 meth = args[cur_arg];
3978 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003979 else if (strcmp(args[cur_arg], "uri") == 0 || strcmp(args[cur_arg], "uri-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003980 if (!*(args[cur_arg+1])) {
3981 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3982 goto error;
3983 }
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02003984 flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
3985 if (strcmp(args[cur_arg], "uri-lf") == 0)
3986 flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02003987 cur_arg++;
3988 uri = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02003989 }
Christopher Faulet907701b2020-04-28 09:37:00 +02003990 else if (strcmp(args[cur_arg], "ver") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003991 if (!*(args[cur_arg+1])) {
3992 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
3993 goto error;
3994 }
3995 cur_arg++;
3996 vsn = args[cur_arg];
3997 }
3998 else if (strcmp(args[cur_arg], "hdr") == 0) {
3999 if (!*args[cur_arg+1] || !*args[cur_arg+2]) {
4000 memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg]);
4001 goto error;
4002 }
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004003
4004 if (strcasecmp(args[cur_arg+1], "host") == 0) {
4005 if (host_hdr >= 0) {
4006 memprintf(errmsg, "'%s' header already defined (previous value is '%s')",
4007 args[cur_arg+1], istptr(hdrs[host_hdr].v));
4008 goto error;
4009 }
4010 host_hdr = i;
4011 }
Christopher Faulet7d765db2020-04-29 11:50:01 +02004012 else if (strcasecmp(args[cur_arg+1], "connection") == 0 ||
4013 strcasecmp(args[cur_arg+1], "content-length") == 0 ||
4014 strcasecmp(args[cur_arg+1], "transfer-encoding") == 0)
4015 goto skip_hdr;
Christopher Faulet13ec87b2020-04-29 11:45:44 +02004016
Christopher Faulet61cc8522020-04-20 14:54:42 +02004017 hdrs[i].n = ist2(args[cur_arg+1], strlen(args[cur_arg+1]));
4018 hdrs[i].v = ist2(args[cur_arg+2], strlen(args[cur_arg+2]));
4019 i++;
Christopher Faulet7d765db2020-04-29 11:50:01 +02004020 skip_hdr:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004021 cur_arg += 2;
4022 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004023 else if (strcmp(args[cur_arg], "body") == 0 || strcmp(args[cur_arg], "body-lf") == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004024 if (!*(args[cur_arg+1])) {
4025 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4026 goto error;
4027 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004028 flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4029 if (strcmp(args[cur_arg], "body-lf") == 0)
4030 flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004031 cur_arg++;
4032 body = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004033 }
4034 else if (strcmp(args[cur_arg], "comment") == 0) {
4035 if (!*(args[cur_arg+1])) {
4036 memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
4037 goto error;
4038 }
4039 cur_arg++;
4040 free(comment);
4041 comment = strdup(args[cur_arg]);
4042 if (!comment) {
4043 memprintf(errmsg, "out of memory");
4044 goto error;
4045 }
4046 }
4047 else {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004048 memprintf(errmsg, "expects 'comment', 'meth', 'uri', 'uri-lf', 'ver', 'hdr', 'body' or 'body-lf'"
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004049 " but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004050 goto error;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004051 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004052 cur_arg++;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004053 }
4054
Christopher Faulet61cc8522020-04-20 14:54:42 +02004055 hdrs[i].n = hdrs[i].v = IST_NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004056
Christopher Faulet61cc8522020-04-20 14:54:42 +02004057 chk = calloc(1, sizeof(*chk));
4058 if (!chk) {
4059 memprintf(errmsg, "out of memory");
4060 goto error;
4061 }
4062 chk->action = TCPCHK_ACT_SEND;
4063 chk->comment = comment; comment = NULL;
4064 chk->send.type = TCPCHK_SEND_HTTP;
4065 chk->send.http.flags = flags;
4066 LIST_INIT(&chk->send.http.hdrs);
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004067
Christopher Faulet61cc8522020-04-20 14:54:42 +02004068 if (meth) {
4069 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
4070 chk->send.http.meth.str.area = strdup(meth);
4071 chk->send.http.meth.str.data = strlen(meth);
4072 if (!chk->send.http.meth.str.area) {
4073 memprintf(errmsg, "out of memory");
4074 goto error;
4075 }
4076 }
4077 if (uri) {
Christopher Faulet7c95f5f2020-05-06 15:06:34 +02004078 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) {
4079 LIST_INIT(&chk->send.http.uri_fmt);
4080 px->conf.args.ctx = ARGC_SRV;
4081 if (!parse_logformat_string(uri, px, &chk->send.http.uri_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4082 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", uri, *errmsg);
4083 goto error;
4084 }
4085 }
4086 else {
4087 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
4088 if (!isttest(chk->send.http.uri)) {
4089 memprintf(errmsg, "out of memory");
4090 goto error;
4091 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004092 }
4093 }
4094 if (vsn) {
4095 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
4096 if (!isttest(chk->send.http.vsn)) {
4097 memprintf(errmsg, "out of memory");
4098 goto error;
4099 }
4100 }
Christopher Fauletb61caf42020-04-21 10:57:42 +02004101 for (i = 0; istlen(hdrs[i].n); i++) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004102 hdr = calloc(1, sizeof(*hdr));
4103 if (!hdr) {
4104 memprintf(errmsg, "out of memory");
4105 goto error;
4106 }
4107 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02004108 hdr->name = istdup(hdrs[i].n);
4109 if (!isttest(hdr->name)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004110 memprintf(errmsg, "out of memory");
4111 goto error;
4112 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004113
Christopher Fauletb61caf42020-04-21 10:57:42 +02004114 ist0(hdrs[i].v);
4115 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 +02004116 goto error;
4117 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
4118 hdr = NULL;
4119 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004120
Christopher Faulet61cc8522020-04-20 14:54:42 +02004121 if (body) {
Christopher Faulet574e7bd2020-05-06 15:38:58 +02004122 if (chk->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) {
4123 LIST_INIT(&chk->send.http.body_fmt);
4124 px->conf.args.ctx = ARGC_SRV;
4125 if (!parse_logformat_string(body, px, &chk->send.http.body_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4126 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", body, *errmsg);
4127 goto error;
4128 }
4129 }
4130 else {
4131 chk->send.http.body = ist2(strdup(body), strlen(body));
4132 if (!isttest(chk->send.http.body)) {
4133 memprintf(errmsg, "out of memory");
4134 goto error;
4135 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004136 }
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004137 }
4138
Christopher Faulet61cc8522020-04-20 14:54:42 +02004139 return chk;
4140
4141 error:
4142 free_tcpcheck_http_hdr(hdr);
4143 free_tcpcheck(chk, 0);
4144 free(comment);
4145 return NULL;
Simon Horman0ba0e4a2015-01-30 11:23:00 +09004146}
4147
Christopher Faulet61cc8522020-04-20 14:54:42 +02004148/* Parses and creates a http-check comment rule. NULL is returned on error */
4149static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
4150 const char *file, int line, char **errmsg)
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004151{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004152 struct tcpcheck_rule *chk = NULL;
4153 char *comment = NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004154
Christopher Faulet61cc8522020-04-20 14:54:42 +02004155 if (!*(args[cur_arg+1])) {
4156 memprintf(errmsg, "expects a string as argument");
4157 goto error;
4158 }
4159 cur_arg++;
4160 comment = strdup(args[cur_arg]);
4161 if (!comment) {
4162 memprintf(errmsg, "out of memory");
4163 goto error;
4164 }
Willy Tarreau04276f32017-01-06 17:41:29 +01004165
Christopher Faulet61cc8522020-04-20 14:54:42 +02004166 chk = calloc(1, sizeof(*chk));
4167 if (!chk) {
4168 memprintf(errmsg, "out of memory");
4169 goto error;
4170 }
4171 chk->action = TCPCHK_ACT_COMMENT;
4172 chk->comment = comment;
4173 return chk;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004174
Christopher Faulet61cc8522020-04-20 14:54:42 +02004175 error:
4176 free(comment);
4177 return NULL;
Baptiste Assmann95db2bc2016-06-13 14:15:41 +02004178}
4179
Christopher Faulet61cc8522020-04-20 14:54:42 +02004180/* Parses and creates a tcp-check or an http-check expect rule. NULL is returned
4181 * on error. <proto> is set to the right protocol flags (covered by the
4182 * TCPCHK_RULES_PROTO_CHK mask).
4183 */
4184static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px,
4185 struct list *rules, unsigned int proto,
4186 const char *file, int line, char **errmsg)
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004187{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004188 struct tcpcheck_rule *prev_check, *chk = NULL;
4189 struct sample_expr *status_expr = NULL;
Christopher Faulet39708192020-05-05 10:47:36 +02004190 char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004191 enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004192 enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
4193 enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
4194 enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02004195 unsigned int flags = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004196 long min_recv = -1;
Christopher Faulet88d939c2020-04-22 15:32:11 +02004197 int inverse = 0;
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004198
Christopher Faulet39708192020-05-05 10:47:36 +02004199 on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02004200 if (!*(args[cur_arg+1])) {
4201 memprintf(errmsg, "expects at least a matching pattern as arguments");
4202 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004203 }
4204
Christopher Faulet61cc8522020-04-20 14:54:42 +02004205 cur_arg++;
4206 while (*(args[cur_arg])) {
4207 int in_pattern = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02004208
Christopher Faulet61cc8522020-04-20 14:54:42 +02004209 rescan:
4210 if (strcmp(args[cur_arg], "min-recv") == 0) {
4211 if (in_pattern) {
4212 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4213 goto error;
4214 }
4215 if (!*(args[cur_arg+1])) {
4216 memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]);
4217 goto error;
4218 }
4219 /* Use an signed integer here because of chksize */
4220 cur_arg++;
4221 min_recv = atol(args[cur_arg]);
4222 if (min_recv < -1 || min_recv > INT_MAX) {
4223 memprintf(errmsg, "'%s' expects -1 or an integer from 0 to INT_MAX" , args[cur_arg-1]);
4224 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02004225 }
4226 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004227 else if (*(args[cur_arg]) == '!') {
4228 in_pattern = 1;
4229 while (*(args[cur_arg]) == '!') {
4230 inverse = !inverse;
4231 args[cur_arg]++;
Christopher Faulete5870d82020-04-15 11:32:03 +02004232 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004233 if (!*(args[cur_arg]))
4234 cur_arg++;
4235 goto rescan;
Christopher Faulete5870d82020-04-15 11:32:03 +02004236 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004237 else if (strcmp(args[cur_arg], "string") == 0 || strcmp(args[cur_arg], "rstring") == 0) {
4238 if (type != TCPCHK_EXPECT_UNDEF) {
4239 memprintf(errmsg, "only on pattern expected");
4240 goto error;
4241 }
4242 if (proto != TCPCHK_RULES_HTTP_CHK)
Christopher Faulet67a23452020-05-05 18:10:01 +02004243 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING : TCPCHK_EXPECT_STRING_REGEX);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004244 else
Christopher Faulet67a23452020-05-05 18:10:01 +02004245 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_BODY : TCPCHK_EXPECT_HTTP_BODY_REGEX);
Christopher Faulete5870d82020-04-15 11:32:03 +02004246
Christopher Faulet61cc8522020-04-20 14:54:42 +02004247 if (!*(args[cur_arg+1])) {
4248 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4249 goto error;
4250 }
4251 cur_arg++;
4252 pattern = args[cur_arg];
Christopher Fauleta202d1d2020-03-26 17:38:49 +01004253 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004254 else if (strcmp(args[cur_arg], "binary") == 0 || strcmp(args[cur_arg], "rbinary") == 0) {
4255 if (proto == TCPCHK_RULES_HTTP_CHK)
4256 goto bad_http_kw;
4257 if (type != TCPCHK_EXPECT_UNDEF) {
4258 memprintf(errmsg, "only on pattern expected");
4259 goto error;
4260 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004261 type = ((*(args[cur_arg]) == 'b') ? TCPCHK_EXPECT_BINARY : TCPCHK_EXPECT_BINARY_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004262
Christopher Faulet61cc8522020-04-20 14:54:42 +02004263 if (!*(args[cur_arg+1])) {
4264 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4265 goto error;
4266 }
4267 cur_arg++;
4268 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004269 }
Christopher Fauletaaab0832020-05-05 15:54:22 +02004270 else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
4271 if (type != TCPCHK_EXPECT_UNDEF) {
4272 memprintf(errmsg, "only on pattern expected");
4273 goto error;
4274 }
4275 if (proto != TCPCHK_RULES_HTTP_CHK)
4276 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
4277 else {
4278 if (*(args[cur_arg]) != 's')
4279 goto bad_http_kw;
4280 type = TCPCHK_EXPECT_HTTP_BODY_LF;
4281 }
4282
4283 if (!*(args[cur_arg+1])) {
4284 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4285 goto error;
4286 }
4287 cur_arg++;
4288 pattern = args[cur_arg];
4289 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004290 else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
4291 if (proto != TCPCHK_RULES_HTTP_CHK)
4292 goto bad_tcp_kw;
4293 if (type != TCPCHK_EXPECT_UNDEF) {
4294 memprintf(errmsg, "only on pattern expected");
4295 goto error;
4296 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004297 type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_HTTP_STATUS : TCPCHK_EXPECT_HTTP_STATUS_REGEX);
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004298
Christopher Faulet61cc8522020-04-20 14:54:42 +02004299 if (!*(args[cur_arg+1])) {
4300 memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
4301 goto error;
4302 }
4303 cur_arg++;
4304 pattern = args[cur_arg];
Christopher Faulet6f2a5e42020-04-01 13:11:41 +02004305 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004306 else if (strcmp(args[cur_arg], "custom") == 0) {
4307 if (in_pattern) {
4308 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4309 goto error;
4310 }
4311 if (type != TCPCHK_EXPECT_UNDEF) {
4312 memprintf(errmsg, "only on pattern expected");
4313 goto error;
4314 }
4315 type = TCPCHK_EXPECT_CUSTOM;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004316 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004317 else if (strcmp(args[cur_arg], "hdr") == 0 || strcmp(args[cur_arg], "fhdr") == 0) {
Christopher Faulet39708192020-05-05 10:47:36 +02004318 int orig_arg = cur_arg;
4319
4320 if (proto != TCPCHK_RULES_HTTP_CHK)
4321 goto bad_tcp_kw;
4322 if (type != TCPCHK_EXPECT_UNDEF) {
4323 memprintf(errmsg, "only on pattern expected");
4324 goto error;
4325 }
4326 type = TCPCHK_EXPECT_HTTP_HEADER;
4327
Christopher Fauletb5594262020-05-05 20:23:13 +02004328 if (strcmp(args[cur_arg], "fhdr") == 0)
4329 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
4330
Christopher Faulet39708192020-05-05 10:47:36 +02004331 /* Parse the name pattern, mandatory */
Christopher Fauletb5594262020-05-05 20:23:13 +02004332 if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) ||
4333 (strcmp(args[cur_arg+1], "name") != 0 && strcmp(args[cur_arg+1], "name-lf") != 0)) {
4334 memprintf(errmsg, "'%s' expects at the name keyword as first argument followed by a pattern",
Christopher Faulet39708192020-05-05 10:47:36 +02004335 args[orig_arg]);
4336 goto error;
4337 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004338
4339 if (strcmp(args[cur_arg+1], "name-lf") == 0)
4340 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
4341
Christopher Faulet39708192020-05-05 10:47:36 +02004342 cur_arg += 2;
4343 if (strcmp(args[cur_arg], "-m") == 0) {
4344 if (!*(args[cur_arg+1])) {
4345 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4346 args[orig_arg], args[cur_arg]);
4347 goto error;
4348 }
4349 if (strcmp(args[cur_arg+1], "str") == 0)
4350 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4351 else if (strcmp(args[cur_arg+1], "beg") == 0)
4352 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
4353 else if (strcmp(args[cur_arg+1], "end") == 0)
4354 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
4355 else if (strcmp(args[cur_arg+1], "sub") == 0)
4356 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004357 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4358 if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4359 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4360 args[orig_arg]);
4361 goto error;
4362 }
Christopher Faulet39708192020-05-05 10:47:36 +02004363 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004364 }
Christopher Faulet39708192020-05-05 10:47:36 +02004365 else {
4366 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4367 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4368 goto error;
4369 }
4370 cur_arg += 2;
4371 }
4372 else
4373 flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
4374 npat = args[cur_arg];
4375
Christopher Fauletb5594262020-05-05 20:23:13 +02004376 if (!*(args[cur_arg+1]) ||
4377 (strcmp(args[cur_arg+1], "value") != 0 && strcmp(args[cur_arg+1], "value-lf") != 0)) {
Christopher Faulet39708192020-05-05 10:47:36 +02004378 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
4379 goto next;
4380 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004381 if (strcmp(args[cur_arg+1], "value-lf") == 0)
4382 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
Christopher Faulet39708192020-05-05 10:47:36 +02004383
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004384 /* Parse the value pattern, optional */
Christopher Fauletb5594262020-05-05 20:23:13 +02004385 if (strcmp(args[cur_arg+2], "-m") == 0) {
4386 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004387 if (!*(args[cur_arg+1])) {
4388 memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
4389 args[orig_arg], args[cur_arg]);
4390 goto error;
4391 }
4392 if (strcmp(args[cur_arg+1], "str") == 0)
4393 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
4394 else if (strcmp(args[cur_arg+1], "beg") == 0)
4395 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
4396 else if (strcmp(args[cur_arg+1], "end") == 0)
4397 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
4398 else if (strcmp(args[cur_arg+1], "sub") == 0)
4399 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
Christopher Fauletb5594262020-05-05 20:23:13 +02004400 else if (strcmp(args[cur_arg+1], "reg") == 0) {
4401 if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4402 memprintf(errmsg, "'%s': log-format string is not supported with a regex matching method",
4403 args[orig_arg]);
4404 goto error;
4405 }
Christopher Faulet39708192020-05-05 10:47:36 +02004406 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
Christopher Fauletb5594262020-05-05 20:23:13 +02004407 }
Christopher Faulet39708192020-05-05 10:47:36 +02004408 else {
4409 memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
4410 args[orig_arg], args[cur_arg], args[cur_arg+1]);
4411 goto error;
4412 }
Christopher Faulet39708192020-05-05 10:47:36 +02004413 }
4414 else
4415 flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
Christopher Faulet39708192020-05-05 10:47:36 +02004416
Christopher Fauletb5594262020-05-05 20:23:13 +02004417 if (!*(args[cur_arg+2])) {
4418 memprintf(errmsg, "'%s' expect a pattern with the value keyword", args[orig_arg]);
4419 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004420 }
Christopher Fauletb5594262020-05-05 20:23:13 +02004421 vpat = args[cur_arg+2];
4422 cur_arg += 2;
Christopher Faulet39708192020-05-05 10:47:36 +02004423 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004424 else if (strcmp(args[cur_arg], "comment") == 0) {
4425 if (in_pattern) {
4426 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4427 goto error;
4428 }
4429 if (!*(args[cur_arg+1])) {
4430 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4431 goto error;
4432 }
4433 cur_arg++;
4434 free(comment);
4435 comment = strdup(args[cur_arg]);
4436 if (!comment) {
4437 memprintf(errmsg, "out of memory");
4438 goto error;
4439 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004440 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004441 else if (strcmp(args[cur_arg], "on-success") == 0) {
4442 if (in_pattern) {
4443 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4444 goto error;
4445 }
4446 if (!*(args[cur_arg+1])) {
4447 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4448 goto error;
4449 }
4450 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004451 on_success_msg = args[cur_arg];
Christopher Fauletd7e63962020-04-17 20:15:59 +02004452 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004453 else if (strcmp(args[cur_arg], "on-error") == 0) {
4454 if (in_pattern) {
4455 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4456 goto error;
4457 }
4458 if (!*(args[cur_arg+1])) {
4459 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4460 goto error;
4461 }
4462 cur_arg++;
Christopher Faulet1941bab2020-05-05 07:55:50 +02004463 on_error_msg = args[cur_arg];
Christopher Faulet61cc8522020-04-20 14:54:42 +02004464 }
4465 else if (strcmp(args[cur_arg], "ok-status") == 0) {
4466 if (in_pattern) {
4467 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4468 goto error;
4469 }
4470 if (!*(args[cur_arg+1])) {
4471 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4472 goto error;
4473 }
4474 if (strcasecmp(args[cur_arg+1], "L7OK") == 0)
4475 ok_st = HCHK_STATUS_L7OKD;
4476 else if (strcasecmp(args[cur_arg+1], "L7OKC") == 0)
4477 ok_st = HCHK_STATUS_L7OKCD;
4478 else if (strcasecmp(args[cur_arg+1], "L6OK") == 0)
4479 ok_st = HCHK_STATUS_L6OK;
4480 else if (strcasecmp(args[cur_arg+1], "L4OK") == 0)
4481 ok_st = HCHK_STATUS_L4OK;
4482 else {
4483 memprintf(errmsg, "'%s' only supports 'L4OK', 'L6OK', 'L7OK' or 'L7OKC' status (got '%s').",
4484 args[cur_arg], args[cur_arg+1]);
4485 goto error;
4486 }
4487 cur_arg++;
4488 }
4489 else if (strcmp(args[cur_arg], "error-status") == 0) {
4490 if (in_pattern) {
4491 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4492 goto error;
4493 }
4494 if (!*(args[cur_arg+1])) {
4495 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4496 goto error;
4497 }
4498 if (strcasecmp(args[cur_arg+1], "L7RSP") == 0)
4499 err_st = HCHK_STATUS_L7RSP;
4500 else if (strcasecmp(args[cur_arg+1], "L7STS") == 0)
4501 err_st = HCHK_STATUS_L7STS;
4502 else if (strcasecmp(args[cur_arg+1], "L6RSP") == 0)
4503 err_st = HCHK_STATUS_L6RSP;
4504 else if (strcasecmp(args[cur_arg+1], "L4CON") == 0)
4505 err_st = HCHK_STATUS_L4CON;
4506 else {
4507 memprintf(errmsg, "'%s' only supports 'L4CON', 'L6RSP', 'L7RSP' or 'L7STS' status (got '%s').",
4508 args[cur_arg], args[cur_arg+1]);
4509 goto error;
4510 }
4511 cur_arg++;
4512 }
4513 else if (strcmp(args[cur_arg], "status-code") == 0) {
4514 int idx = 0;
Christopher Fauletd7e63962020-04-17 20:15:59 +02004515
Christopher Faulet61cc8522020-04-20 14:54:42 +02004516 if (in_pattern) {
4517 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4518 goto error;
4519 }
4520 if (!*(args[cur_arg+1])) {
4521 memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
4522 goto error;
4523 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004524
Christopher Faulet61cc8522020-04-20 14:54:42 +02004525 cur_arg++;
4526 release_sample_expr(status_expr);
4527 px->conf.args.ctx = ARGC_SRV;
4528 status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
4529 file, line, errmsg, &px->conf.args, NULL);
4530 if (!status_expr) {
4531 memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
4532 goto error;
4533 }
4534 if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
4535 memprintf(errmsg, "error detected while parsing status-code expression : "
4536 " fetch method '%s' extracts information from '%s', "
4537 "none of which is available here.\n",
4538 args[cur_arg], sample_src_names(status_expr->fetch->use));
4539 goto error;
4540 }
4541 px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
4542 }
4543 else if (strcmp(args[cur_arg], "tout-status") == 0) {
4544 if (in_pattern) {
4545 memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
4546 goto error;
4547 }
4548 if (!*(args[cur_arg+1])) {
4549 memprintf(errmsg, "'%s' expects a string as argument", args[cur_arg]);
4550 goto error;
4551 }
4552 if (strcasecmp(args[cur_arg+1], "L7TOUT") == 0)
4553 tout_st = HCHK_STATUS_L7TOUT;
4554 else if (strcasecmp(args[cur_arg+1], "L6TOUT") == 0)
4555 tout_st = HCHK_STATUS_L6TOUT;
4556 else if (strcasecmp(args[cur_arg+1], "L4TOUT") == 0)
4557 tout_st = HCHK_STATUS_L4TOUT;
4558 else {
4559 memprintf(errmsg, "'%s' only supports 'L4TOUT', 'L6TOUT' or 'L7TOUT' status (got '%s').",
4560 args[cur_arg], args[cur_arg+1]);
4561 goto error;
4562 }
4563 cur_arg++;
4564 }
4565 else {
4566 if (proto == TCPCHK_RULES_HTTP_CHK) {
4567 bad_http_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004568 memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
Christopher Fauletb5594262020-05-05 20:23:13 +02004569 "'[!]rstatus', [!]hdr, [!]fhdr or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004570 }
4571 else {
4572 bad_tcp_kw:
Christopher Fauletaaab0832020-05-05 15:54:22 +02004573 memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
4574 "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004575 }
4576 goto error;
4577 }
Christopher Faulet39708192020-05-05 10:47:36 +02004578 next:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004579 cur_arg++;
4580 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004581
Christopher Faulet61cc8522020-04-20 14:54:42 +02004582 chk = calloc(1, sizeof(*chk));
4583 if (!chk) {
4584 memprintf(errmsg, "out of memory");
4585 goto error;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004586 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004587 chk->action = TCPCHK_ACT_EXPECT;
4588 LIST_INIT(&chk->expect.onerror_fmt);
4589 LIST_INIT(&chk->expect.onsuccess_fmt);
4590 chk->comment = comment; comment = NULL;
4591 chk->expect.type = type;
4592 chk->expect.min_recv = min_recv;
Christopher Faulet39708192020-05-05 10:47:36 +02004593 chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004594 chk->expect.ok_status = ok_st;
4595 chk->expect.err_status = err_st;
4596 chk->expect.tout_status = tout_st;
4597 chk->expect.status_expr = status_expr; status_expr = NULL;
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004598
Christopher Faulet61cc8522020-04-20 14:54:42 +02004599 if (on_success_msg) {
4600 px->conf.args.ctx = ARGC_SRV;
4601 if (!parse_logformat_string(on_success_msg, px, &chk->expect.onsuccess_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4602 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_success_msg, *errmsg);
4603 goto error;
4604 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004605 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004606 if (on_error_msg) {
4607 px->conf.args.ctx = ARGC_SRV;
4608 if (!parse_logformat_string(on_error_msg, px, &chk->expect.onerror_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4609 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", on_error_msg, *errmsg);
4610 goto error;
4611 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004612 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02004613
Christopher Faulet61cc8522020-04-20 14:54:42 +02004614 switch (chk->expect.type) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02004615 case TCPCHK_EXPECT_HTTP_STATUS: {
4616 const char *p = pattern;
4617 unsigned int c1,c2;
4618
4619 chk->expect.codes.codes = NULL;
4620 chk->expect.codes.num = 0;
4621 while (1) {
4622 c1 = c2 = read_uint(&p, pattern + strlen(pattern));
4623 if (*p == '-') {
4624 p++;
4625 c2 = read_uint(&p, pattern + strlen(pattern));
4626 }
4627 if (c1 > c2) {
4628 memprintf(errmsg, "invalid range of status codes '%s'", pattern);
4629 goto error;
4630 }
4631
4632 chk->expect.codes.num++;
4633 chk->expect.codes.codes = my_realloc2(chk->expect.codes.codes,
4634 chk->expect.codes.num * sizeof(*chk->expect.codes.codes));
4635 if (!chk->expect.codes.codes) {
4636 memprintf(errmsg, "out of memory");
4637 goto error;
4638 }
4639 chk->expect.codes.codes[chk->expect.codes.num-1][0] = c1;
4640 chk->expect.codes.codes[chk->expect.codes.num-1][1] = c2;
4641
4642 if (*p == '\0')
4643 break;
4644 if (*p != ',') {
4645 memprintf(errmsg, "invalid character '%c' in the list of status codes", *p);
4646 goto error;
4647 }
4648 p++;
4649 }
4650 break;
4651 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004652 case TCPCHK_EXPECT_STRING:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004653 case TCPCHK_EXPECT_HTTP_BODY:
4654 chk->expect.data = ist2(strdup(pattern), strlen(pattern));
Christopher Fauletb61caf42020-04-21 10:57:42 +02004655 if (!isttest(chk->expect.data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004656 memprintf(errmsg, "out of memory");
4657 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004658 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004659 break;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004660 case TCPCHK_EXPECT_BINARY: {
Christopher Faulet9c2cb2d2020-04-28 16:40:41 +02004661 int len = chk->expect.data.len;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004662
4663 if (parse_binary(pattern, &chk->expect.data.ptr, &len, errmsg) == 0) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004664 memprintf(errmsg, "invalid binary string (%s)", *errmsg);
4665 goto error;
4666 }
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02004667 chk->expect.data.len = len;
4668 break;
4669 }
Christopher Faulet67a23452020-05-05 18:10:01 +02004670 case TCPCHK_EXPECT_STRING_REGEX:
4671 case TCPCHK_EXPECT_BINARY_REGEX:
4672 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
4673 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulet88d939c2020-04-22 15:32:11 +02004674 chk->expect.regex = regex_comp(pattern, 1, 0, errmsg);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004675 if (!chk->expect.regex)
4676 goto error;
Christopher Faulet39708192020-05-05 10:47:36 +02004677 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +02004678
4679 case TCPCHK_EXPECT_STRING_LF:
4680 case TCPCHK_EXPECT_BINARY_LF:
4681 case TCPCHK_EXPECT_HTTP_BODY_LF:
4682 LIST_INIT(&chk->expect.fmt);
4683 px->conf.args.ctx = ARGC_SRV;
4684 if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4685 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
4686 goto error;
4687 }
4688 break;
4689
Christopher Faulet39708192020-05-05 10:47:36 +02004690 case TCPCHK_EXPECT_HTTP_HEADER:
4691 if (!npat) {
4692 memprintf(errmsg, "unexpected error, undefined header name pattern");
4693 goto error;
4694 }
4695 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
4696 chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
4697 if (!chk->expect.hdr.name_re)
4698 goto error;
4699 }
4700 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
4701 px->conf.args.ctx = ARGC_SRV;
4702 LIST_INIT(&chk->expect.hdr.name_fmt);
4703 if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4704 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4705 goto error;
4706 }
4707 }
4708 else {
4709 chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
4710 if (!isttest(chk->expect.hdr.name)) {
4711 memprintf(errmsg, "out of memory");
4712 goto error;
4713 }
4714 }
4715
4716 if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
4717 chk->expect.hdr.value = IST_NULL;
4718 break;
4719 }
4720
4721 if (!vpat) {
4722 memprintf(errmsg, "unexpected error, undefined header value pattern");
4723 goto error;
4724 }
4725 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
4726 chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
4727 if (!chk->expect.hdr.value_re)
4728 goto error;
4729 }
4730 else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
4731 px->conf.args.ctx = ARGC_SRV;
4732 LIST_INIT(&chk->expect.hdr.value_fmt);
4733 if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
4734 memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
4735 goto error;
4736 }
4737 }
4738 else {
4739 chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
4740 if (!isttest(chk->expect.hdr.value)) {
4741 memprintf(errmsg, "out of memory");
4742 goto error;
4743 }
4744 }
4745
Christopher Faulet61cc8522020-04-20 14:54:42 +02004746 break;
4747 case TCPCHK_EXPECT_CUSTOM:
4748 chk->expect.custom = NULL; /* Must be defined by the caller ! */
4749 break;
4750 case TCPCHK_EXPECT_UNDEF:
Christopher Faulet61cc8522020-04-20 14:54:42 +02004751 memprintf(errmsg, "pattern not found");
4752 goto error;
Christopher Faulet5d503fc2020-03-30 20:34:34 +02004753 }
Christopher Faulet8892e5d2020-03-26 19:48:20 +01004754
Christopher Faulet61cc8522020-04-20 14:54:42 +02004755 /* All tcp-check expect points back to the first inverse expect rule in
4756 * a chain of one or more expect rule, potentially itself.
4757 */
4758 chk->expect.head = chk;
4759 list_for_each_entry_rev(prev_check, rules, list) {
4760 if (prev_check->action == TCPCHK_ACT_EXPECT) {
4761 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
4762 chk->expect.head = prev_check;
4763 continue;
4764 }
4765 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
4766 break;
Christopher Faulet404f9192020-04-09 23:13:54 +02004767 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004768 return chk;
4769
4770 error:
4771 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004772 free(comment);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004773 release_sample_expr(status_expr);
Christopher Faulet404f9192020-04-09 23:13:54 +02004774 return NULL;
4775}
4776
Christopher Faulet61cc8522020-04-20 14:54:42 +02004777/* Overwrites fields of the old http send rule with those of the new one. When
4778 * replaced, old values are freed and replaced by the new ones. New values are
4779 * not copied but transferred. At the end <new> should be empty and can be
4780 * safely released. This function never fails.
4781 */
4782static void tcpcheck_overwrite_send_http_rule(struct tcpcheck_rule *old, struct tcpcheck_rule *new)
Christopher Faulet404f9192020-04-09 23:13:54 +02004783{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004784 struct logformat_node *lf, *lfb;
4785 struct tcpcheck_http_hdr *hdr, *bhdr;
Christopher Faulet404f9192020-04-09 23:13:54 +02004786
Christopher Faulet404f9192020-04-09 23:13:54 +02004787
Christopher Faulet61cc8522020-04-20 14:54:42 +02004788 if (new->send.http.meth.str.area) {
4789 free(old->send.http.meth.str.area);
4790 old->send.http.meth.meth = new->send.http.meth.meth;
4791 old->send.http.meth.str.area = new->send.http.meth.str.area;
4792 old->send.http.meth.str.data = new->send.http.meth.str.data;
4793 new->send.http.meth.str = BUF_NULL;
Christopher Faulet404f9192020-04-09 23:13:54 +02004794 }
4795
Christopher Faulet61cc8522020-04-20 14:54:42 +02004796 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && isttest(new->send.http.uri)) {
4797 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004798 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004799 else
4800 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4801 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_URI_FMT;
4802 old->send.http.uri = new->send.http.uri;
4803 new->send.http.uri = IST_NULL;
4804 }
4805 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT) && !LIST_ISEMPTY(&new->send.http.uri_fmt)) {
4806 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_URI_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004807 istfree(&old->send.http.uri);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004808 else
4809 free_tcpcheck_fmt(&old->send.http.uri_fmt);
4810 old->send.http.flags |= TCPCHK_SND_HTTP_FL_URI_FMT;
4811 LIST_INIT(&old->send.http.uri_fmt);
4812 list_for_each_entry_safe(lf, lfb, &new->send.http.uri_fmt, list) {
4813 LIST_DEL(&lf->list);
4814 LIST_ADDQ(&old->send.http.uri_fmt, &lf->list);
4815 }
4816 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004817
Christopher Faulet61cc8522020-04-20 14:54:42 +02004818 if (isttest(new->send.http.vsn)) {
Christopher Fauletb61caf42020-04-21 10:57:42 +02004819 istfree(&old->send.http.vsn);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004820 old->send.http.vsn = new->send.http.vsn;
4821 new->send.http.vsn = IST_NULL;
4822 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004823
Christopher Faulet61cc8522020-04-20 14:54:42 +02004824 free_tcpcheck_http_hdrs(&old->send.http.hdrs);
4825 list_for_each_entry_safe(hdr, bhdr, &new->send.http.hdrs, list) {
4826 LIST_DEL(&hdr->list);
4827 LIST_ADDQ(&old->send.http.hdrs, &hdr->list);
Christopher Faulet404f9192020-04-09 23:13:54 +02004828 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02004829
4830 if (!(new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && isttest(new->send.http.body)) {
4831 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004832 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004833 else
4834 free_tcpcheck_fmt(&old->send.http.body_fmt);
4835 old->send.http.flags &= ~TCPCHK_SND_HTTP_FL_BODY_FMT;
4836 old->send.http.body = new->send.http.body;
4837 new->send.http.body = IST_NULL;
4838 }
4839 else if ((new->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT) && !LIST_ISEMPTY(&new->send.http.body_fmt)) {
4840 if (!(old->send.http.flags & TCPCHK_SND_HTTP_FL_BODY_FMT))
Christopher Fauletb61caf42020-04-21 10:57:42 +02004841 istfree(&old->send.http.body);
Christopher Faulet61cc8522020-04-20 14:54:42 +02004842 else
4843 free_tcpcheck_fmt(&old->send.http.body_fmt);
4844 old->send.http.flags |= TCPCHK_SND_HTTP_FL_BODY_FMT;
4845 LIST_INIT(&old->send.http.body_fmt);
4846 list_for_each_entry_safe(lf, lfb, &new->send.http.body_fmt, list) {
4847 LIST_DEL(&lf->list);
4848 LIST_ADDQ(&old->send.http.body_fmt, &lf->list);
4849 }
4850 }
Christopher Faulet404f9192020-04-09 23:13:54 +02004851}
4852
Christopher Faulet61cc8522020-04-20 14:54:42 +02004853/* Internal function used to add an http-check rule in a list during the config
4854 * parsing step. Depending on its type, and the previously inserted rules, a
4855 * specific action may be performed or an error may be reported. This functions
4856 * returns 1 on success and 0 on error and <errmsg> is filled with the error
4857 * message.
4858 */
4859static int tcpcheck_add_http_rule(struct tcpcheck_rule *chk, struct tcpcheck_rules *rules, char **errmsg)
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004860{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004861 struct tcpcheck_rule *r;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004862
Christopher Faulet61cc8522020-04-20 14:54:42 +02004863 /* the implicit send rule coming from an "option httpchk" line must be
4864 * merged with the first explici http-check send rule, if
4865 * any. Depdending the declaration order some tests are required.
4866 *
4867 * Some tests is also required for other kinds of http-check rules to be
4868 * sure the ruleset remains valid.
4869 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004870
Christopher Faulet61cc8522020-04-20 14:54:42 +02004871 if (chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004872 /* Tries to add an implicit http-check send rule from an "option httpchk" line.
Christopher Faulet61cc8522020-04-20 14:54:42 +02004873 * First, the first rule is retrieved, skipping the first CONNECT, if any, and
4874 * following tests are performed :
4875 *
4876 * 1- If there is no such rule or if it is not a send rule, the implicit send
4877 * rule is pushed in front of the ruleset
4878 *
4879 * 2- If it is another implicit send rule, it is replaced with the new one.
4880 *
4881 * 3- Otherwise, it means it is an explicit send rule. In this case we merge
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05004882 * both, overwriting the old send rule (the explicit one) with info of the
Christopher Faulet61cc8522020-04-20 14:54:42 +02004883 * new send rule (the implicit one).
4884 */
4885 r = get_first_tcpcheck_rule(rules);
4886 if (r && r->action == TCPCHK_ACT_CONNECT)
4887 r = get_next_tcpcheck_rule(rules, r);
4888 if (!r || r->action != TCPCHK_ACT_SEND)
4889 LIST_ADD(rules->list, &chk->list);
4890 else if (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT) {
4891 LIST_DEL(&r->list);
4892 free_tcpcheck(r, 0);
4893 LIST_ADD(rules->list, &chk->list);
4894 }
4895 else {
4896 tcpcheck_overwrite_send_http_rule(r, chk);
4897 free_tcpcheck(chk, 0);
4898 }
4899 }
4900 else {
4901 /* Tries to add an explicit http-check rule. First of all we check the typefo the
4902 * last inserted rule to be sure it is valid. Then for send rule, we try to merge it
4903 * with an existing implicit send rule, if any. At the end, if there is no error,
4904 * the rule is appended to the list.
4905 */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004906
Christopher Faulet61cc8522020-04-20 14:54:42 +02004907 r = get_last_tcpcheck_rule(rules);
4908 if (!r || (r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)))
4909 /* no error */;
4910 else if (r->action != TCPCHK_ACT_CONNECT && chk->action == TCPCHK_ACT_SEND) {
4911 memprintf(errmsg, "unable to add http-check send rule at step %d (missing connect rule).",
4912 chk->index+1);
4913 return 0;
4914 }
Christopher Faulet1a200d62020-05-05 07:59:23 +02004915 else if (r->action != TCPCHK_ACT_SEND && r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_EXPECT) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02004916 memprintf(errmsg, "unable to add http-check expect rule at step %d (missing send rule).",
4917 chk->index+1);
4918 return 0;
4919 }
4920 else if (r->action != TCPCHK_ACT_EXPECT && chk->action == TCPCHK_ACT_CONNECT) {
4921 memprintf(errmsg, "unable to add http-check connect rule at step %d (missing expect rule).",
4922 chk->index+1);
4923 return 0;
4924 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004925
Christopher Faulet61cc8522020-04-20 14:54:42 +02004926 if (chk->action == TCPCHK_ACT_SEND) {
4927 r = get_first_tcpcheck_rule(rules);
4928 if (r && r->action == TCPCHK_ACT_SEND && (r->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
4929 tcpcheck_overwrite_send_http_rule(r, chk);
4930 free_tcpcheck(chk, 0);
4931 LIST_DEL(&r->list);
4932 r->send.http.flags &= ~TCPCHK_SND_HTTP_FROM_OPT;
4933 chk = r;
4934 }
4935 }
4936 LIST_ADDQ(rules->list, &chk->list);
4937 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004938 return 1;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004939}
4940
Christopher Faulet61cc8522020-04-20 14:54:42 +02004941/**************************************************************************/
4942/************************** Init/deinit checks ****************************/
4943/**************************************************************************/
4944static const char *init_check(struct check *check, int type)
4945{
4946 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004947
Christopher Faulet61cc8522020-04-20 14:54:42 +02004948 b_reset(&check->bi); check->bi.size = global.tune.chksize;
4949 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004950
Christopher Faulet61cc8522020-04-20 14:54:42 +02004951 check->bi.area = calloc(check->bi.size, sizeof(char));
4952 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02004953
Christopher Faulet61cc8522020-04-20 14:54:42 +02004954 if (!check->bi.area || !check->bo.area)
4955 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004956
Christopher Faulet61cc8522020-04-20 14:54:42 +02004957 check->wait_list.tasklet = tasklet_new();
4958 if (!check->wait_list.tasklet)
4959 return "out of memory while allocating check tasklet";
4960 check->wait_list.events = 0;
4961 check->wait_list.tasklet->process = event_srv_chk_io;
4962 check->wait_list.tasklet->context = check;
4963 return NULL;
4964}
4965
4966void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004967{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004968 task_destroy(check->task);
4969 if (check->wait_list.tasklet)
4970 tasklet_free(check->wait_list.tasklet);
4971
4972 free(check->bi.area);
4973 free(check->bo.area);
4974 if (check->cs) {
4975 free(check->cs->conn);
4976 check->cs->conn = NULL;
4977 cs_free(check->cs);
4978 check->cs = NULL;
4979 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004980}
4981
Christopher Faulet61cc8522020-04-20 14:54:42 +02004982/* manages a server health-check. Returns the time the task accepts to wait, or
4983 * TIME_ETERNITY for infinity.
4984 */
4985static struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004986{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004987 struct check *check = context;
4988
4989 if (check->type == PR_O2_EXT_CHK)
4990 return process_chk_proc(t, context, state);
4991 return process_chk_conn(t, context, state);
4992
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004993}
4994
Christopher Faulet61cc8522020-04-20 14:54:42 +02004995
4996static int start_check_task(struct check *check, int mininter,
4997 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01004998{
Christopher Faulet61cc8522020-04-20 14:54:42 +02004999 struct task *t;
5000 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005001
Christopher Faulet61cc8522020-04-20 14:54:42 +02005002 if (check->type == PR_O2_EXT_CHK)
5003 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005004
Christopher Faulet61cc8522020-04-20 14:54:42 +02005005 /* task for the check */
5006 if ((t = task_new(thread_mask)) == NULL) {
5007 ha_alert("Starting [%s:%s] check: out of memory.\n",
5008 check->server->proxy->id, check->server->id);
5009 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005010 }
5011
Christopher Faulet61cc8522020-04-20 14:54:42 +02005012 check->task = t;
5013 t->process = process_chk;
5014 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005015
Christopher Faulet61cc8522020-04-20 14:54:42 +02005016 if (mininter < srv_getinter(check))
5017 mininter = srv_getinter(check);
5018
5019 if (global.max_spread_checks && mininter > global.max_spread_checks)
5020 mininter = global.max_spread_checks;
5021
5022 /* check this every ms */
5023 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
5024 check->start = now;
5025 task_queue(t);
5026
5027 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005028}
5029
Christopher Faulet61cc8522020-04-20 14:54:42 +02005030/* updates the server's weight during a warmup stage. Once the final weight is
5031 * reached, the task automatically stops. Note that any server status change
5032 * must have updated s->last_change accordingly.
5033 */
5034static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005035{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005036 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005037
Christopher Faulet61cc8522020-04-20 14:54:42 +02005038 /* by default, plan on stopping the task */
5039 t->expire = TICK_ETERNITY;
5040 if ((s->next_admin & SRV_ADMF_MAINT) ||
5041 (s->next_state != SRV_ST_STARTING))
5042 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02005043
Christopher Faulet61cc8522020-04-20 14:54:42 +02005044 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005045
Christopher Faulet61cc8522020-04-20 14:54:42 +02005046 /* recalculate the weights and update the state */
5047 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02005048
Christopher Faulet61cc8522020-04-20 14:54:42 +02005049 /* probably that we can refill this server with a bit more connections */
5050 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02005051
Christopher Faulet61cc8522020-04-20 14:54:42 +02005052 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02005053
Christopher Faulet61cc8522020-04-20 14:54:42 +02005054 /* get back there in 1 second or 1/20th of the slowstart interval,
5055 * whichever is greater, resulting in small 5% steps.
5056 */
5057 if (s->next_state == SRV_ST_STARTING)
5058 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
5059 return t;
5060}
5061
5062/*
5063 * Start health-check.
5064 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
5065 */
5066static int start_checks()
5067{
5068
5069 struct proxy *px;
5070 struct server *s;
5071 struct task *t;
5072 int nbcheck=0, mininter=0, srvpos=0;
5073
5074 /* 0- init the dummy frontend used to create all checks sessions */
5075 init_new_proxy(&checks_fe);
5076 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
5077 checks_fe.mode = PR_MODE_TCP;
5078 checks_fe.maxconn = 0;
5079 checks_fe.conn_retries = CONN_RETRIES;
5080 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
5081 checks_fe.timeout.client = TICK_ETERNITY;
5082
5083 /* 1- count the checkers to run simultaneously.
5084 * We also determine the minimum interval among all of those which
5085 * have an interval larger than SRV_CHK_INTER_THRES. This interval
5086 * will be used to spread their start-up date. Those which have
5087 * a shorter interval will start independently and will not dictate
5088 * too short an interval for all others.
5089 */
5090 for (px = proxies_list; px; px = px->next) {
5091 for (s = px->srv; s; s = s->next) {
5092 if (s->slowstart) {
5093 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5094 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
5095 return ERR_ALERT | ERR_FATAL;
5096 }
5097 /* We need a warmup task that will be called when the server
5098 * state switches from down to up.
5099 */
5100 s->warmup = t;
5101 t->process = server_warmup;
5102 t->context = s;
5103 /* server can be in this state only because of */
5104 if (s->next_state == SRV_ST_STARTING)
5105 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 +02005106 }
5107
Christopher Faulet61cc8522020-04-20 14:54:42 +02005108 if (s->check.state & CHK_ST_CONFIGURED) {
5109 nbcheck++;
5110 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
5111 (!mininter || mininter > srv_getinter(&s->check)))
5112 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02005113 }
5114
Christopher Faulet61cc8522020-04-20 14:54:42 +02005115 if (s->agent.state & CHK_ST_CONFIGURED) {
5116 nbcheck++;
5117 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
5118 (!mininter || mininter > srv_getinter(&s->agent)))
5119 mininter = srv_getinter(&s->agent);
5120 }
Christopher Faulet5c288742020-03-31 08:15:58 +02005121 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005122 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005123
Christopher Faulet61cc8522020-04-20 14:54:42 +02005124 if (!nbcheck)
5125 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005126
Christopher Faulet61cc8522020-04-20 14:54:42 +02005127 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02005128
Christopher Faulet61cc8522020-04-20 14:54:42 +02005129 /*
5130 * 2- start them as far as possible from each others. For this, we will
5131 * start them after their interval set to the min interval divided by
5132 * the number of servers, weighted by the server's position in the list.
5133 */
5134 for (px = proxies_list; px; px = px->next) {
5135 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
5136 if (init_pid_list()) {
5137 ha_alert("Starting [%s] check: out of memory.\n", px->id);
5138 return ERR_ALERT | ERR_FATAL;
5139 }
5140 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02005141
Christopher Faulet61cc8522020-04-20 14:54:42 +02005142 for (s = px->srv; s; s = s->next) {
5143 /* A task for the main check */
5144 if (s->check.state & CHK_ST_CONFIGURED) {
5145 if (s->check.type == PR_O2_EXT_CHK) {
5146 if (!prepare_external_check(&s->check))
5147 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02005148 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005149 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
5150 return ERR_ALERT | ERR_FATAL;
5151 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02005152 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005153
Christopher Faulet61cc8522020-04-20 14:54:42 +02005154 /* A task for a auxiliary agent check */
5155 if (s->agent.state & CHK_ST_CONFIGURED) {
5156 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
5157 return ERR_ALERT | ERR_FATAL;
5158 }
5159 srvpos++;
5160 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005161 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005162 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005163 return 0;
5164}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005165
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005166
Christopher Faulet61cc8522020-04-20 14:54:42 +02005167/*
5168 * Return value:
5169 * the port to be used for the health check
5170 * 0 in case no port could be found for the check
5171 */
5172static int srv_check_healthcheck_port(struct check *chk)
5173{
5174 int i = 0;
5175 struct server *srv = NULL;
5176
5177 srv = chk->server;
5178
5179 /* by default, we use the health check port ocnfigured */
5180 if (chk->port > 0)
5181 return chk->port;
5182
5183 /* try to get the port from check_core.addr if check.port not set */
5184 i = get_host_port(&chk->addr);
5185 if (i > 0)
5186 return i;
5187
5188 /* try to get the port from server address */
5189 /* prevent MAPPORTS from working at this point, since checks could
5190 * not be performed in such case (MAPPORTS impose a relative ports
5191 * based on live traffic)
5192 */
5193 if (srv->flags & SRV_F_MAPPORTS)
5194 return 0;
5195
5196 i = srv->svc_port; /* by default */
5197 if (i > 0)
5198 return i;
5199
5200 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005201}
5202
Christopher Faulet61cc8522020-04-20 14:54:42 +02005203/* Initializes an health-check attached to the server <srv>. Non-zero is returned
5204 * if an error occurred.
5205 */
5206static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005207{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005208 const char *err;
5209 struct tcpcheck_rule *r;
5210 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005211
Christopher Faulet61cc8522020-04-20 14:54:42 +02005212 if (!srv->do_check)
5213 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005214
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005215
Christopher Faulet61cc8522020-04-20 14:54:42 +02005216 /* If neither a port nor an addr was specified and no check transport
5217 * layer is forced, then the transport layer used by the checks is the
5218 * same as for the production traffic. Otherwise we use raw_sock by
5219 * default, unless one is specified.
5220 */
5221 if (!srv->check.port && !is_addr(&srv->check.addr)) {
5222 if (!srv->check.use_ssl && srv->use_ssl != -1) {
5223 srv->check.use_ssl = srv->use_ssl;
5224 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005225 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005226 else if (srv->check.use_ssl == 1)
5227 srv->check.xprt = xprt_get(XPRT_SSL);
5228 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005229 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02005230 else if (srv->check.use_ssl == 1)
5231 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005232
Christopher Faulet12882cf2020-04-23 15:50:18 +02005233 /* Inherit the mux protocol from the server if not already defined for
5234 * the check
5235 */
5236 if (srv->mux_proto && !srv->check.mux_proto)
5237 srv->check.mux_proto = srv->mux_proto;
5238
Christopher Faulet61cc8522020-04-20 14:54:42 +02005239 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005240
Christopher Faulet61cc8522020-04-20 14:54:42 +02005241 /* We need at least a service port, a check port or the first tcp-check
5242 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
5243 */
5244 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
5245 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
5246 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02005247
Christopher Faulet61cc8522020-04-20 14:54:42 +02005248 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
5249 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
5250 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5251 ret |= ERR_ALERT | ERR_ABORT;
5252 goto out;
5253 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005254
Christopher Faulet61cc8522020-04-20 14:54:42 +02005255 /* search the first action (connect / send / expect) in the list */
5256 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
5257 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
5258 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
5259 "nor tcp_check rule 'connect' with port information.\n",
5260 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5261 ret |= ERR_ALERT | ERR_ABORT;
5262 goto out;
5263 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005264
Christopher Faulet61cc8522020-04-20 14:54:42 +02005265 /* scan the tcp-check ruleset to ensure a port has been configured */
5266 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
5267 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
5268 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
5269 "and a tcp_check rule 'connect' with no port information.\n",
5270 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5271 ret |= ERR_ALERT | ERR_ABORT;
5272 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005273 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005274 }
5275
Christopher Faulet61cc8522020-04-20 14:54:42 +02005276 init:
5277 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
5278 struct tcpcheck_ruleset *rs = NULL;
5279 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
5280 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005281
Christopher Faulet61cc8522020-04-20 14:54:42 +02005282 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
5283 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02005284
Christopher Faulet61cc8522020-04-20 14:54:42 +02005285 rs = find_tcpcheck_ruleset("*tcp-check");
5286 if (!rs) {
5287 rs = create_tcpcheck_ruleset("*tcp-check");
5288 if (rs == NULL) {
5289 ha_alert("config: %s '%s': out of memory.\n",
5290 proxy_type_str(srv->proxy), srv->proxy->id);
5291 ret |= ERR_ALERT | ERR_FATAL;
5292 goto out;
5293 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005294 }
5295
Christopher Faulet61cc8522020-04-20 14:54:42 +02005296 free_tcpcheck_vars(&rules->preset_vars);
5297 rules->list = &rs->rules;
5298 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005299 }
5300
Christopher Faulet61cc8522020-04-20 14:54:42 +02005301 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
5302 if (err) {
5303 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
5304 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5305 ret |= ERR_ALERT | ERR_ABORT;
5306 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005307 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005308 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
5309 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02005310
Christopher Faulet61cc8522020-04-20 14:54:42 +02005311 out:
5312 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005313}
5314
Christopher Faulet61cc8522020-04-20 14:54:42 +02005315/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
5316 * if an error occurred.
5317 */
5318static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02005319{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005320 struct tcpcheck_rule *chk;
5321 const char *err;
5322 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005323
Christopher Faulet61cc8522020-04-20 14:54:42 +02005324 if (!srv->do_agent)
5325 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005326
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005327 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005328 * implicit one is inserted before all others.
5329 */
5330 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
5331 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5332 chk = calloc(1, sizeof(*chk));
5333 if (!chk) {
5334 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
5335 " to agent-check for server '%s' (out of memory).\n",
5336 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
5337 ret |= ERR_ALERT | ERR_FATAL;
5338 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005339 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005340 chk->action = TCPCHK_ACT_CONNECT;
5341 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5342 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02005343 }
5344
Christopher Faulete5870d82020-04-15 11:32:03 +02005345
Christopher Faulet61cc8522020-04-20 14:54:42 +02005346 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
5347 if (err) {
5348 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
5349 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
5350 ret |= ERR_ALERT | ERR_ABORT;
5351 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005352 }
5353
Christopher Faulet61cc8522020-04-20 14:54:42 +02005354 if (!srv->agent.inter)
5355 srv->agent.inter = srv->check.inter;
5356
5357 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
5358 global.maxsock++;
5359
5360 out:
5361 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005362}
5363
Christopher Faulet61cc8522020-04-20 14:54:42 +02005364/* Check tcp-check health-check configuration for the proxy <px>. */
5365static int check_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005366{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005367 struct tcpcheck_rule *chk, *back;
5368 char *comment = NULL, *errmsg = NULL;
5369 enum tcpcheck_rule_type prev_action = TCPCHK_ACT_COMMENT;
5370 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005371
Christopher Faulet61cc8522020-04-20 14:54:42 +02005372 if (!(px->cap & PR_CAP_BE) || (px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
5373 deinit_proxy_tcpcheck(px);
5374 goto out;
5375 }
5376
5377 free(px->check_command);
5378 free(px->check_path);
5379 px->check_command = px->check_path = NULL;
5380
5381 if (!px->tcpcheck_rules.list) {
5382 ha_alert("config : proxy '%s' : tcp-check configured but no ruleset defined.\n", px->id);
5383 ret |= ERR_ALERT | ERR_FATAL;
5384 goto out;
5385 }
5386
5387 /* HTTP ruleset only : */
5388 if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
5389 struct tcpcheck_rule *next;
5390
5391 /* move remaining implicit send rule from "option httpchk" line to the right place.
5392 * If such rule exists, it must be the first one. In this case, the rule is moved
5393 * after the first connect rule, if any. Otherwise, nothing is done.
5394 */
5395 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5396 if (chk && chk->action == TCPCHK_ACT_SEND && (chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT)) {
5397 next = get_next_tcpcheck_rule(&px->tcpcheck_rules, chk);
5398 if (next && next->action == TCPCHK_ACT_CONNECT) {
5399 LIST_DEL(&chk->list);
5400 LIST_ADD(&next->list, &chk->list);
5401 chk->index = next->index;
5402 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005403 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005404
5405 /* add implicit expect rule if the last one is a send. It is inherited from previous
5406 * versions where the http expect rule was optional. Now it is possible to chained
5407 * send/expect rules but the last expect may still be implicit.
5408 */
5409 chk = get_last_tcpcheck_rule(&px->tcpcheck_rules);
5410 if (chk && chk->action == TCPCHK_ACT_SEND) {
Christopher Faulet8021a5f2020-04-24 13:53:12 +02005411 next = parse_tcpcheck_expect((char *[]){"http-check", "expect", "status", "200-399", ""},
Christopher Faulet61cc8522020-04-20 14:54:42 +02005412 1, px, px->tcpcheck_rules.list, TCPCHK_RULES_HTTP_CHK,
5413 px->conf.file, px->conf.line, &errmsg);
5414 if (!next) {
5415 ha_alert("config : proxy '%s': unable to add implicit http-check expect rule "
5416 "(%s).\n", px->id, errmsg);
5417 free(errmsg);
5418 ret |= ERR_ALERT | ERR_FATAL;
5419 goto out;
5420 }
5421 LIST_ADDQ(px->tcpcheck_rules.list, &next->list);
5422 next->index = chk->index;
Christopher Faulete5870d82020-04-15 11:32:03 +02005423 }
5424 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005425
5426 /* For all ruleset: */
5427
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05005428 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02005429 * implicit one is inserted before all others.
5430 */
5431 chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
5432 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
5433 chk = calloc(1, sizeof(*chk));
5434 if (!chk) {
5435 ha_alert("config : proxy '%s': unable to add implicit tcp-check connect rule "
5436 "(out of memory).\n", px->id);
5437 ret |= ERR_ALERT | ERR_FATAL;
5438 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02005439 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005440 chk->action = TCPCHK_ACT_CONNECT;
5441 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
5442 LIST_ADD(px->tcpcheck_rules.list, &chk->list);
5443 }
5444
5445 /* Remove all comment rules. To do so, when a such rule is found, the
5446 * comment is assigned to the following rule(s).
5447 */
5448 list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
5449 if (chk->action != prev_action && prev_action != TCPCHK_ACT_COMMENT) {
5450 free(comment);
5451 comment = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005452 }
5453
Christopher Faulet61cc8522020-04-20 14:54:42 +02005454 prev_action = chk->action;
5455 switch (chk->action) {
5456 case TCPCHK_ACT_COMMENT:
5457 free(comment);
5458 comment = chk->comment;
5459 LIST_DEL(&chk->list);
5460 free(chk);
5461 break;
5462 case TCPCHK_ACT_CONNECT:
5463 if (!chk->comment && comment)
5464 chk->comment = strdup(comment);
5465 /* fall though */
5466 case TCPCHK_ACT_ACTION_KW:
5467 free(comment);
5468 comment = NULL;
5469 break;
5470 case TCPCHK_ACT_SEND:
5471 case TCPCHK_ACT_EXPECT:
5472 if (!chk->comment && comment)
5473 chk->comment = strdup(comment);
5474 break;
Christopher Faulete5870d82020-04-15 11:32:03 +02005475 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005476 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005477 free(comment);
5478 comment = NULL;
5479
5480 out:
5481 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02005482}
5483
Christopher Faulet61cc8522020-04-20 14:54:42 +02005484void deinit_proxy_tcpcheck(struct proxy *px)
Christopher Faulete5870d82020-04-15 11:32:03 +02005485{
Christopher Faulet61cc8522020-04-20 14:54:42 +02005486 free_tcpcheck_vars(&px->tcpcheck_rules.preset_vars);
5487 px->tcpcheck_rules.flags = 0;
5488 px->tcpcheck_rules.list = NULL;
5489}
Christopher Faulete5870d82020-04-15 11:32:03 +02005490
Christopher Faulet61cc8522020-04-20 14:54:42 +02005491static void deinit_srv_check(struct server *srv)
5492{
5493 if (srv->check.state & CHK_ST_CONFIGURED)
5494 free_check(&srv->check);
5495 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
5496 srv->do_check = 0;
5497}
Christopher Faulete5870d82020-04-15 11:32:03 +02005498
Christopher Faulet61cc8522020-04-20 14:54:42 +02005499
5500static void deinit_srv_agent_check(struct server *srv)
5501{
5502 if (srv->agent.tcpcheck_rules) {
5503 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
5504 free(srv->agent.tcpcheck_rules);
5505 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02005506 }
Christopher Faulete5870d82020-04-15 11:32:03 +02005507
Christopher Faulet61cc8522020-04-20 14:54:42 +02005508 if (srv->agent.state & CHK_ST_CONFIGURED)
5509 free_check(&srv->agent);
5510
5511 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
5512 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02005513}
5514
Christopher Faulet61cc8522020-04-20 14:54:42 +02005515static void deinit_tcpchecks()
Christopher Faulete5870d82020-04-15 11:32:03 +02005516{
Christopher Fauletd7cee712020-04-21 13:45:00 +02005517 struct tcpcheck_ruleset *rs;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005518 struct tcpcheck_rule *r, *rb;
Christopher Fauletd7cee712020-04-21 13:45:00 +02005519 struct ebpt_node *node, *next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005520
Christopher Fauletd7cee712020-04-21 13:45:00 +02005521 node = ebpt_first(&shared_tcpchecks);
5522 while (node) {
5523 next = ebpt_next(node);
5524 ebpt_delete(node);
5525 free(node->key);
5526 rs = container_of(node, typeof(*rs), node);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005527 list_for_each_entry_safe(r, rb, &rs->rules, list) {
5528 LIST_DEL(&r->list);
5529 free_tcpcheck(r, 0);
5530 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005531 free(rs);
Christopher Fauletd7cee712020-04-21 13:45:00 +02005532 node = next;
Christopher Faulete5870d82020-04-15 11:32:03 +02005533 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005534}
Christopher Faulete5870d82020-04-15 11:32:03 +02005535
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005536
Christopher Faulet61cc8522020-04-20 14:54:42 +02005537REGISTER_POST_SERVER_CHECK(init_srv_check);
5538REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
5539REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
5540REGISTER_POST_CHECK(start_checks);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005541
Christopher Faulet61cc8522020-04-20 14:54:42 +02005542REGISTER_SERVER_DEINIT(deinit_srv_check);
5543REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
5544REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
5545REGISTER_POST_DEINIT(deinit_tcpchecks);
Christopher Faulete5870d82020-04-15 11:32:03 +02005546
Christopher Faulet61cc8522020-04-20 14:54:42 +02005547/**************************************************************************/
5548/****************************** Email alerts ******************************/
5549/* NOTE: It may be pertinent to use an applet to handle email alerts */
5550/* instead of a tcp-check ruleset */
5551/**************************************************************************/
5552void email_alert_free(struct email_alert *alert)
5553{
5554 struct tcpcheck_rule *rule, *back;
Christopher Faulete5870d82020-04-15 11:32:03 +02005555
Christopher Faulet61cc8522020-04-20 14:54:42 +02005556 if (!alert)
5557 return;
5558
5559 if (alert->rules.list) {
5560 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
5561 LIST_DEL(&rule->list);
5562 free_tcpcheck(rule, 1);
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005563 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005564 free_tcpcheck_vars(&alert->rules.preset_vars);
5565 free(alert->rules.list);
5566 alert->rules.list = NULL;
5567 }
5568 pool_free(pool_head_email_alert, alert);
5569}
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005570
Christopher Faulet61cc8522020-04-20 14:54:42 +02005571static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
5572{
5573 struct check *check = context;
5574 struct email_alertq *q;
5575 struct email_alert *alert;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005576
Christopher Faulet61cc8522020-04-20 14:54:42 +02005577 q = container_of(check, typeof(*q), check);
5578
5579 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5580 while (1) {
5581 if (!(check->state & CHK_ST_ENABLED)) {
5582 if (LIST_ISEMPTY(&q->email_alerts)) {
5583 /* All alerts processed, queue the task */
5584 t->expire = TICK_ETERNITY;
5585 task_queue(t);
5586 goto end;
Christopher Faulet98cc57c2020-04-01 20:52:31 +02005587 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005588
5589 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
5590 LIST_DEL(&alert->list);
5591 t->expire = now_ms;
5592 check->tcpcheck_rules = &alert->rules;
5593 check->status = HCHK_STATUS_INI;
5594 check->state |= CHK_ST_ENABLED;
Christopher Fauletcf80f2f2020-04-01 11:04:52 +02005595 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005596
5597 process_chk(t, context, state);
5598 if (check->state & CHK_ST_INPROGRESS)
5599 break;
5600
5601 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
5602 email_alert_free(alert);
5603 check->tcpcheck_rules = NULL;
5604 check->server = NULL;
5605 check->state &= ~CHK_ST_ENABLED;
5606 }
5607 end:
5608 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5609 return t;
5610}
5611
5612/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
5613 *
5614 * The function returns 1 in success case, otherwise, it returns 0 and err is
5615 * filled.
5616 */
5617int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
5618{
5619 struct mailer *mailer;
5620 struct email_alertq *queues;
5621 const char *err_str;
5622 int i = 0;
5623
5624 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
5625 memprintf(err, "out of memory while allocating mailer alerts queues");
5626 goto fail_no_queue;
5627 }
5628
5629 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
5630 struct email_alertq *q = &queues[i];
5631 struct check *check = &q->check;
5632 struct task *t;
5633
5634 LIST_INIT(&q->email_alerts);
5635 HA_SPIN_INIT(&q->lock);
5636 check->inter = mls->timeout.mail;
5637 check->rise = DEF_AGENT_RISETIME;
5638 check->proxy = p;
5639 check->fall = DEF_AGENT_FALLTIME;
5640 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
5641 memprintf(err, "%s", err_str);
5642 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005643 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005644
5645 check->xprt = mailer->xprt;
5646 check->addr = mailer->addr;
5647 check->port = get_host_port(&mailer->addr);
5648
5649 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
5650 memprintf(err, "out of memory while allocating mailer alerts task");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005651 goto error;
5652 }
5653
Christopher Faulet61cc8522020-04-20 14:54:42 +02005654 check->task = t;
5655 t->process = process_email_alert;
5656 t->context = check;
5657
5658 /* check this in one ms */
5659 t->expire = TICK_ETERNITY;
5660 check->start = now;
5661 task_queue(t);
5662 }
5663
5664 mls->users++;
5665 free(p->email_alert.mailers.name);
5666 p->email_alert.mailers.m = mls;
5667 p->email_alert.queues = queues;
5668 return 0;
5669
5670 error:
5671 for (i = 0; i < mls->count; i++) {
5672 struct email_alertq *q = &queues[i];
5673 struct check *check = &q->check;
5674
5675 free_check(check);
5676 }
5677 free(queues);
5678 fail_no_queue:
5679 return 1;
5680}
5681
5682static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
5683{
5684 struct tcpcheck_rule *tcpcheck, *prev_check;
5685 struct tcpcheck_expect *expect;
5686
5687 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5688 return 0;
5689 memset(tcpcheck, 0, sizeof(*tcpcheck));
5690 tcpcheck->action = TCPCHK_ACT_EXPECT;
5691
5692 expect = &tcpcheck->expect;
5693 expect->type = TCPCHK_EXPECT_STRING;
5694 LIST_INIT(&expect->onerror_fmt);
5695 LIST_INIT(&expect->onsuccess_fmt);
5696 expect->ok_status = HCHK_STATUS_L7OKD;
5697 expect->err_status = HCHK_STATUS_L7RSP;
5698 expect->tout_status = HCHK_STATUS_L7TOUT;
5699 expect->data = ist2(strdup(str), strlen(str));
Christopher Fauletb61caf42020-04-21 10:57:42 +02005700 if (!isttest(expect->data)) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005701 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5702 return 0;
5703 }
5704
5705 /* All tcp-check expect points back to the first inverse expect rule
5706 * in a chain of one or more expect rule, potentially itself.
5707 */
5708 tcpcheck->expect.head = tcpcheck;
5709 list_for_each_entry_rev(prev_check, rules->list, list) {
5710 if (prev_check->action == TCPCHK_ACT_EXPECT) {
5711 if (prev_check->expect.flags & TCPCHK_EXPT_FL_INV)
5712 tcpcheck->expect.head = prev_check;
5713 continue;
5714 }
5715 if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
5716 break;
5717 }
5718 LIST_ADDQ(rules->list, &tcpcheck->list);
5719 return 1;
5720}
5721
5722static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
5723{
5724 struct tcpcheck_rule *tcpcheck;
5725 struct tcpcheck_send *send;
5726 const char *in;
5727 char *dst;
5728 int i;
5729
5730 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5731 return 0;
5732 memset(tcpcheck, 0, sizeof(*tcpcheck));
5733 tcpcheck->action = TCPCHK_ACT_SEND;
5734
5735 send = &tcpcheck->send;
5736 send->type = TCPCHK_SEND_STRING;
5737
5738 for (i = 0; strs[i]; i++)
5739 send->data.len += strlen(strs[i]);
5740
Christopher Fauletb61caf42020-04-21 10:57:42 +02005741 send->data.ptr = malloc(istlen(send->data) + 1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005742 if (!isttest(send->data)) {
5743 pool_free(pool_head_tcpcheck_rule, tcpcheck);
5744 return 0;
5745 }
5746
Christopher Fauletb61caf42020-04-21 10:57:42 +02005747 dst = istptr(send->data);
Christopher Faulet61cc8522020-04-20 14:54:42 +02005748 for (i = 0; strs[i]; i++)
5749 for (in = strs[i]; (*dst = *in++); dst++);
5750 *dst = 0;
5751
5752 LIST_ADDQ(rules->list, &tcpcheck->list);
5753 return 1;
5754}
5755
5756static int enqueue_one_email_alert(struct proxy *p, struct server *s,
5757 struct email_alertq *q, const char *msg)
5758{
5759 struct email_alert *alert;
5760 struct tcpcheck_rule *tcpcheck;
5761 struct check *check = &q->check;
5762
5763 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
5764 goto error;
5765 LIST_INIT(&alert->list);
5766 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
5767 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
5768 if (!alert->rules.list)
5769 goto error;
5770 LIST_INIT(alert->rules.list);
5771 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
5772 alert->srv = s;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005773
Christopher Faulet61cc8522020-04-20 14:54:42 +02005774 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
5775 goto error;
5776 memset(tcpcheck, 0, sizeof(*tcpcheck));
5777 tcpcheck->action = TCPCHK_ACT_CONNECT;
5778 tcpcheck->comment = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005779
Christopher Faulet61cc8522020-04-20 14:54:42 +02005780 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005781
Christopher Faulet61cc8522020-04-20 14:54:42 +02005782 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005783 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005784
5785 {
5786 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
5787 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5788 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005789 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005790
Christopher Faulet61cc8522020-04-20 14:54:42 +02005791 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5792 goto error;
5793
5794 {
5795 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
5796 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005797 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005798 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005799
5800 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5801 goto error;
5802
5803 {
5804 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
5805 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005806 goto error;
Christopher Fauletbe52b4d2020-04-01 16:30:22 +02005807 }
5808
Christopher Faulet61cc8522020-04-20 14:54:42 +02005809 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
5810 goto error;
5811
5812 {
5813 const char * const strs[2] = { "DATA\r\n" };
5814 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005815 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005816 }
5817
5818 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
5819 goto error;
5820
5821 {
5822 struct tm tm;
5823 char datestr[48];
5824 const char * const strs[18] = {
5825 "From: ", p->email_alert.from, "\r\n",
5826 "To: ", p->email_alert.to, "\r\n",
5827 "Date: ", datestr, "\r\n",
5828 "Subject: [HAproxy Alert] ", msg, "\r\n",
5829 "\r\n",
5830 msg, "\r\n",
5831 "\r\n",
5832 ".\r\n",
5833 NULL
5834 };
5835
5836 get_localtime(date.tv_sec, &tm);
5837
5838 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005839 goto error;
5840 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02005841
5842 if (!add_tcpcheck_send_strs(&alert->rules, strs))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005843 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005844 }
5845
5846 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005847 goto error;
Christopher Faulet61cc8522020-04-20 14:54:42 +02005848
5849 {
5850 const char * const strs[2] = { "QUIT\r\n" };
5851 if (!add_tcpcheck_send_strs(&alert->rules, strs))
5852 goto error;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005853 }
5854
Christopher Faulet61cc8522020-04-20 14:54:42 +02005855 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
5856 goto error;
5857
5858 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
5859 task_wakeup(check->task, TASK_WOKEN_MSG);
5860 LIST_ADDQ(&q->email_alerts, &alert->list);
5861 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
5862 return 1;
5863
5864error:
5865 email_alert_free(alert);
5866 return 0;
5867}
5868
5869static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
5870{
5871 int i;
5872 struct mailer *mailer;
5873
5874 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
5875 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
5876 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
5877 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
5878 return;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005879 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005880 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005881
Christopher Faulet61cc8522020-04-20 14:54:42 +02005882 return;
5883}
5884
5885/*
5886 * Send email alert if configured.
5887 */
5888void send_email_alert(struct server *s, int level, const char *format, ...)
5889{
5890 va_list argp;
5891 char buf[1024];
5892 int len;
5893 struct proxy *p = s->proxy;
5894
5895 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
5896 return;
5897
5898 va_start(argp, format);
5899 len = vsnprintf(buf, sizeof(buf), format, argp);
5900 va_end(argp);
5901
5902 if (len < 0 || len >= sizeof(buf)) {
5903 ha_alert("Email alert [%s] could not format message\n", p->id);
5904 return;
5905 }
5906
5907 enqueue_email_alert(p, s, buf);
5908}
5909
5910/**************************************************************************/
5911/************************** Check sample fetches **************************/
5912/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005913
Christopher Faulet61cc8522020-04-20 14:54:42 +02005914static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005915 { /* END */ },
5916}};
5917
5918INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
5919
5920
5921/**************************************************************************/
5922/************************ Check's parsing functions ***********************/
5923/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005924/* Parses the "tcp-check" proxy keyword */
5925static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
5926 struct proxy *defpx, const char *file, int line,
5927 char **errmsg)
5928{
Christopher Faulet404f9192020-04-09 23:13:54 +02005929 struct tcpcheck_ruleset *rs = NULL;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005930 struct tcpcheck_rule *chk = NULL;
Gaetan Rivet5301b012020-02-25 17:19:17 +01005931 int index, cur_arg, ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005932
5933 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
5934 ret = 1;
5935
Christopher Faulet404f9192020-04-09 23:13:54 +02005936 /* Deduce the ruleset name from the proxy info */
5937 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
5938 ((curpx == defpx) ? "defaults" : curpx->id),
5939 curpx->conf.file, curpx->conf.line);
Christopher Faulet5d503fc2020-03-30 20:34:34 +02005940
Christopher Faulet61cc8522020-04-20 14:54:42 +02005941 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005942 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02005943 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02005944 if (rs == NULL) {
5945 memprintf(errmsg, "out of memory.\n");
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005946 goto error;
5947 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005948 }
5949
Gaetan Rivet5301b012020-02-25 17:19:17 +01005950 index = 0;
Christopher Faulet404f9192020-04-09 23:13:54 +02005951 if (!LIST_ISEMPTY(&rs->rules)) {
5952 chk = LIST_PREV(&rs->rules, typeof(chk), list);
Gaetan Rivet5301b012020-02-25 17:19:17 +01005953 index = chk->index + 1;
5954 }
5955
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005956 cur_arg = 1;
5957 if (strcmp(args[cur_arg], "connect") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005958 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletb50b3e62020-05-05 18:43:43 +02005959 else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0 ||
5960 strcmp(args[cur_arg], "send-lf") == 0 || strcmp(args[cur_arg], "send-binary-lf") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005961 chk = parse_tcpcheck_send(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005962 else if (strcmp(args[cur_arg], "expect") == 0)
Christopher Faulete5870d82020-04-15 11:32:03 +02005963 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, 0, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005964 else if (strcmp(args[cur_arg], "comment") == 0)
Christopher Faulet404f9192020-04-09 23:13:54 +02005965 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005966 else {
Gaetan Rivet707b52f2020-02-21 18:14:59 +01005967 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
5968
5969 if (!kw) {
5970 action_kw_tcp_check_build_list(&trash);
5971 memprintf(errmsg, "'%s' only supports 'comment', 'connect', 'send', 'send-binary', 'expect'"
5972 "%s%s. but got '%s'",
5973 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
5974 goto error;
5975 }
Christopher Faulet404f9192020-04-09 23:13:54 +02005976 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005977 }
5978
5979 if (!chk) {
5980 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
5981 goto error;
5982 }
Christopher Faulet528f4812020-04-28 10:47:28 +02005983 ret = (ret || (*errmsg != NULL)); /* Handle warning */
Christopher Fauletfd6c2292020-03-25 18:20:15 +01005984
5985 /* No error: add the tcp-check rule in the list */
Gaetan Rivet5301b012020-02-25 17:19:17 +01005986 chk->index = index;
Christopher Faulet404f9192020-04-09 23:13:54 +02005987 LIST_ADDQ(&rs->rules, &chk->list);
5988
5989 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +02005990 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02005991 /* Use this ruleset if the proxy already has tcp-check enabled */
5992 curpx->tcpcheck_rules.list = &rs->rules;
5993 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_TCP_RS;
5994 }
5995 else {
5996 /* mark this ruleset as unused for now */
5997 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_TCP_RS;
5998 }
5999
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006000 return ret;
6001
6002 error:
Christopher Faulet404f9192020-04-09 23:13:54 +02006003 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006004 free_tcpcheck_ruleset(rs);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01006005 return -1;
6006}
6007
Christopher Faulet51b129f2020-04-09 15:54:18 +02006008/* Parses the "http-check" proxy keyword */
6009static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
6010 struct proxy *defpx, const char *file, int line,
6011 char **errmsg)
6012{
Christopher Faulete5870d82020-04-15 11:32:03 +02006013 struct tcpcheck_ruleset *rs = NULL;
6014 struct tcpcheck_rule *chk = NULL;
6015 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006016
6017 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
6018 ret = 1;
6019
6020 cur_arg = 1;
6021 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
6022 /* enable a graceful server shutdown on an HTTP 404 response */
6023 curpx->options |= PR_O_DISABLE404;
6024 if (too_many_args(1, args, errmsg, NULL))
6025 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006026 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006027 }
6028 else if (strcmp(args[cur_arg], "send-state") == 0) {
6029 /* enable emission of the apparent state of a server in HTTP checks */
6030 curpx->options2 |= PR_O2_CHK_SNDST;
6031 if (too_many_args(1, args, errmsg, NULL))
6032 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02006033 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006034 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006035
Christopher Faulete5870d82020-04-15 11:32:03 +02006036 /* Deduce the ruleset name from the proxy info */
6037 chunk_printf(&trash, "*http-check-%s_%s-%d",
6038 ((curpx == defpx) ? "defaults" : curpx->id),
6039 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006040
Christopher Faulet61cc8522020-04-20 14:54:42 +02006041 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006042 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006043 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02006044 if (rs == NULL) {
6045 memprintf(errmsg, "out of memory.\n");
6046 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006047 }
6048 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006049
Christopher Faulete5870d82020-04-15 11:32:03 +02006050 index = 0;
6051 if (!LIST_ISEMPTY(&rs->rules)) {
6052 chk = LIST_PREV(&rs->rules, typeof(chk), list);
6053 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
6054 index = chk->index + 1;
6055 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006056
Christopher Faulete5870d82020-04-15 11:32:03 +02006057 if (strcmp(args[cur_arg], "connect") == 0)
6058 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6059 else if (strcmp(args[cur_arg], "send") == 0)
6060 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6061 else if (strcmp(args[cur_arg], "expect") == 0)
6062 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
6063 file, line, errmsg);
6064 else if (strcmp(args[cur_arg], "comment") == 0)
6065 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
6066 else {
6067 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006068
Christopher Faulete5870d82020-04-15 11:32:03 +02006069 if (!kw) {
6070 action_kw_tcp_check_build_list(&trash);
6071 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
6072 " 'send', 'expect'%s%s. but got '%s'",
6073 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
6074 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006075 }
Christopher Faulete5870d82020-04-15 11:32:03 +02006076 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
6077 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02006078
Christopher Faulete5870d82020-04-15 11:32:03 +02006079 if (!chk) {
6080 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6081 goto error;
6082 }
6083 ret = (*errmsg != NULL); /* Handle warning */
6084
6085 chk->index = index;
6086 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
6087 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
6088 /* Use this ruleset if the proxy already has http-check enabled */
6089 curpx->tcpcheck_rules.list = &rs->rules;
6090 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
6091 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
6092 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
6093 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02006094 goto error;
6095 }
6096 }
6097 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02006098 /* mark this ruleset as unused for now */
6099 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
6100 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006101 }
6102
Christopher Faulete5870d82020-04-15 11:32:03 +02006103 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02006104 return ret;
6105
6106 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02006107 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006108 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02006109 return -1;
6110}
6111
Christopher Faulete9111b62020-04-09 18:12:08 +02006112/* Parses the "external-check" proxy keyword */
6113static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
6114 struct proxy *defpx, const char *file, int line,
6115 char **errmsg)
6116{
6117 int cur_arg, ret = 0;
6118
6119 cur_arg = 1;
6120 if (!*(args[cur_arg])) {
6121 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
6122 goto error;
6123 }
6124
6125 if (strcmp(args[cur_arg], "command") == 0) {
6126 if (too_many_args(2, args, errmsg, NULL))
6127 goto error;
6128 if (!*(args[cur_arg+1])) {
6129 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6130 goto error;
6131 }
6132 free(curpx->check_command);
6133 curpx->check_command = strdup(args[cur_arg+1]);
6134 }
6135 else if (strcmp(args[cur_arg], "path") == 0) {
6136 if (too_many_args(2, args, errmsg, NULL))
6137 goto error;
6138 if (!*(args[cur_arg+1])) {
6139 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
6140 goto error;
6141 }
6142 free(curpx->check_path);
6143 curpx->check_path = strdup(args[cur_arg+1]);
6144 }
6145 else {
6146 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
6147 args[0], args[1]);
6148 goto error;
6149 }
6150
6151 ret = (*errmsg != NULL); /* Handle warning */
6152 return ret;
6153
6154error:
6155 return -1;
6156}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006157
Christopher Faulet430e4802020-04-09 15:28:16 +02006158/* Parses the "option tcp-check" proxy keyword */
6159int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6160 const char *file, int line)
6161{
Christopher Faulet404f9192020-04-09 23:13:54 +02006162 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02006163 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6164 int err_code = 0;
6165
6166 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6167 err_code |= ERR_WARN;
6168
6169 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6170 goto out;
6171
Christopher Faulet404f9192020-04-09 23:13:54 +02006172 curpx->options2 &= ~PR_O2_CHK_ANY;
6173 curpx->options2 |= PR_O2_TCPCHK_CHK;
6174
Christopher Fauletd7e63962020-04-17 20:15:59 +02006175 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02006176 /* If a tcp-check rulesset is already set, do nothing */
6177 if (rules->list)
6178 goto out;
6179
6180 /* If a tcp-check ruleset is waiting to be used for the current proxy,
6181 * get it.
6182 */
6183 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
6184 goto curpx_ruleset;
6185
6186 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
6187 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006188 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006189 if (rs)
6190 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02006191 }
6192
Christopher Faulet404f9192020-04-09 23:13:54 +02006193 curpx_ruleset:
6194 /* Deduce the ruleset name from the proxy info */
6195 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
6196 ((curpx == defpx) ? "defaults" : curpx->id),
6197 curpx->conf.file, curpx->conf.line);
6198
Christopher Faulet61cc8522020-04-20 14:54:42 +02006199 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006200 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02006201 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02006202 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02006203 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6204 goto error;
6205 }
Christopher Faulet430e4802020-04-09 15:28:16 +02006206 }
6207
Christopher Faulet404f9192020-04-09 23:13:54 +02006208 ruleset_found:
6209 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02006210 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02006211 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02006212
6213 out:
6214 return err_code;
6215
6216 error:
6217 err_code |= ERR_ALERT | ERR_FATAL;
6218 goto out;
6219}
Christopher Faulet33f05df2020-04-01 11:08:50 +02006220
6221/* Parses the "option redis-check" proxy keyword */
6222int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6223 const char *file, int line)
6224{
6225 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
6226 static char *redis_res = "+PONG\r\n";
6227
6228 struct tcpcheck_ruleset *rs = NULL;
6229 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6230 struct tcpcheck_rule *chk;
6231 char *errmsg = NULL;
6232 int err_code = 0;
6233
6234 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6235 err_code |= ERR_WARN;
6236
6237 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6238 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006239
6240 curpx->options2 &= ~PR_O2_CHK_ANY;
6241 curpx->options2 |= PR_O2_TCPCHK_CHK;
6242
6243 free_tcpcheck_vars(&rules->preset_vars);
6244 rules->list = NULL;
6245 rules->flags = 0;
6246
Christopher Faulet61cc8522020-04-20 14:54:42 +02006247 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006248 if (rs)
6249 goto ruleset_found;
6250
Christopher Faulet61cc8522020-04-20 14:54:42 +02006251 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02006252 if (rs == NULL) {
6253 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6254 goto error;
6255 }
6256
6257 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
6258 1, curpx, &rs->rules, file, line, &errmsg);
6259 if (!chk) {
6260 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6261 goto error;
6262 }
6263 chk->index = 0;
6264 LIST_ADDQ(&rs->rules, &chk->list);
6265
6266 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
6267 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006268 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02006269 "on-success", "Redis server is ok",
6270 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006271 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006272 if (!chk) {
6273 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6274 goto error;
6275 }
6276 chk->index = 1;
6277 LIST_ADDQ(&rs->rules, &chk->list);
6278
Christopher Faulet33f05df2020-04-01 11:08:50 +02006279 ruleset_found:
6280 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006281 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02006282
6283 out:
6284 free(errmsg);
6285 return err_code;
6286
6287 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006288 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02006289 err_code |= ERR_ALERT | ERR_FATAL;
6290 goto out;
6291}
6292
Christopher Faulet811f78c2020-04-01 11:10:27 +02006293
6294/* Parses the "option ssl-hello-chk" proxy keyword */
6295int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6296 const char *file, int line)
6297{
6298 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
6299 * ssl-hello-chk option to ensure that the remote server speaks SSL.
6300 *
6301 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
6302 */
6303 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05006304 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02006305 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
6306 "0079" /* ContentLength : 0x79 bytes after this one */
6307 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
6308 "000075" /* HandshakeLength : 0x75 bytes after this one */
6309 "0300" /* Hello Version : 0x0300 = v3 */
6310 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
6311 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
6312 "00" /* Session ID length : empty (no session ID) */
6313 "004E" /* Cipher Suite Length : 78 bytes after this one */
6314 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
6315 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
6316 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
6317 "000D" "000E" "000F" "0010" /* various bit lengths, */
6318 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
6319 "0015" "0016" "0017" "0018"
6320 "0019" "001A" "001B" "002F"
6321 "0030" "0031" "0032" "0033"
6322 "0034" "0035" "0036" "0037"
6323 "0038" "0039" "003A"
6324 "01" /* Compression Length : 0x01 = 1 byte for types */
6325 "00" /* Compression Type : 0x00 = NULL compression */
6326 };
6327
6328 struct tcpcheck_ruleset *rs = NULL;
6329 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6330 struct tcpcheck_rule *chk;
6331 char *errmsg = NULL;
6332 int err_code = 0;
6333
6334 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6335 err_code |= ERR_WARN;
6336
6337 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6338 goto out;
6339
Christopher Faulet811f78c2020-04-01 11:10:27 +02006340 curpx->options2 &= ~PR_O2_CHK_ANY;
6341 curpx->options2 |= PR_O2_TCPCHK_CHK;
6342
6343 free_tcpcheck_vars(&rules->preset_vars);
6344 rules->list = NULL;
6345 rules->flags = 0;
6346
Christopher Faulet61cc8522020-04-20 14:54:42 +02006347 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006348 if (rs)
6349 goto ruleset_found;
6350
Christopher Faulet61cc8522020-04-20 14:54:42 +02006351 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02006352 if (rs == NULL) {
6353 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6354 goto error;
6355 }
6356
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006357 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02006358 1, curpx, &rs->rules, file, line, &errmsg);
6359 if (!chk) {
6360 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6361 goto error;
6362 }
6363 chk->index = 0;
6364 LIST_ADDQ(&rs->rules, &chk->list);
6365
6366 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02006367 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02006368 "error-status", "L6RSP", "tout-status", "L6TOUT",
6369 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006370 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006371 if (!chk) {
6372 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6373 goto error;
6374 }
6375 chk->index = 1;
6376 LIST_ADDQ(&rs->rules, &chk->list);
6377
Christopher Faulet811f78c2020-04-01 11:10:27 +02006378 ruleset_found:
6379 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006380 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02006381
6382 out:
6383 free(errmsg);
6384 return err_code;
6385
6386 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006387 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02006388 err_code |= ERR_ALERT | ERR_FATAL;
6389 goto out;
6390}
6391
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006392/* Parses the "option smtpchk" proxy keyword */
6393int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6394 const char *file, int line)
6395{
6396 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
6397
6398 struct tcpcheck_ruleset *rs = NULL;
6399 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6400 struct tcpcheck_rule *chk;
6401 struct tcpcheck_var *var = NULL;
6402 char *cmd = NULL, *errmsg = NULL;
6403 int err_code = 0;
6404
6405 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6406 err_code |= ERR_WARN;
6407
6408 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6409 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006410
6411 curpx->options2 &= ~PR_O2_CHK_ANY;
6412 curpx->options2 |= PR_O2_TCPCHK_CHK;
6413
6414 free_tcpcheck_vars(&rules->preset_vars);
6415 rules->list = NULL;
6416 rules->flags = 0;
6417
6418 cur_arg += 2;
6419 if (*args[cur_arg] && *args[cur_arg+1] &&
6420 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
6421 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
6422 if (cmd)
6423 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
6424 }
6425 else {
6426 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
6427 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
6428 cmd = strdup("HELO localhost");
6429 }
6430
Christopher Fauletb61caf42020-04-21 10:57:42 +02006431 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006432 if (cmd == NULL || var == NULL) {
6433 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6434 goto error;
6435 }
6436 var->data.type = SMP_T_STR;
6437 var->data.u.str.area = cmd;
6438 var->data.u.str.data = strlen(cmd);
6439 LIST_INIT(&var->list);
6440 LIST_ADDQ(&rules->preset_vars, &var->list);
6441 cmd = NULL;
6442 var = NULL;
6443
Christopher Faulet61cc8522020-04-20 14:54:42 +02006444 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006445 if (rs)
6446 goto ruleset_found;
6447
Christopher Faulet61cc8522020-04-20 14:54:42 +02006448 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006449 if (rs == NULL) {
6450 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6451 goto error;
6452 }
6453
6454 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6455 1, curpx, &rs->rules, file, line, &errmsg);
6456 if (!chk) {
6457 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6458 goto error;
6459 }
6460 chk->index = 0;
6461 LIST_ADDQ(&rs->rules, &chk->list);
6462
6463 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
6464 "min-recv", "4",
6465 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006466 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006467 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006468 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006469 if (!chk) {
6470 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6471 goto error;
6472 }
6473 chk->index = 1;
6474 LIST_ADDQ(&rs->rules, &chk->list);
6475
6476 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
6477 "min-recv", "4",
6478 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006479 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6480 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006481 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006482 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006483 if (!chk) {
6484 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6485 goto error;
6486 }
6487 chk->index = 2;
6488 LIST_ADDQ(&rs->rules, &chk->list);
6489
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006490 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006491 1, curpx, &rs->rules, file, line, &errmsg);
6492 if (!chk) {
6493 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6494 goto error;
6495 }
6496 chk->index = 3;
6497 LIST_ADDQ(&rs->rules, &chk->list);
6498
6499 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
6500 "min-recv", "4",
6501 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02006502 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6503 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
6504 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006505 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006506 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006507 if (!chk) {
6508 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6509 goto error;
6510 }
6511 chk->index = 4;
6512 LIST_ADDQ(&rs->rules, &chk->list);
6513
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006514 ruleset_found:
6515 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006516 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006517
6518 out:
6519 free(errmsg);
6520 return err_code;
6521
6522 error:
6523 free(cmd);
6524 free(var);
6525 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006526 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02006527 err_code |= ERR_ALERT | ERR_FATAL;
6528 goto out;
6529}
Christopher Faulet811f78c2020-04-01 11:10:27 +02006530
Christopher Fauletce355072020-04-02 11:44:39 +02006531/* Parses the "option pgsql-check" proxy keyword */
6532int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6533 const char *file, int line)
6534{
6535 static char pgsql_req[] = {
6536 "%[var(check.plen),htonl,hex]" /* The packet length*/
6537 "00030000" /* the version 3.0 */
6538 "7573657200" /* "user" key */
6539 "%[var(check.username),hex]00" /* the username */
6540 "00"
6541 };
6542
6543 struct tcpcheck_ruleset *rs = NULL;
6544 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6545 struct tcpcheck_rule *chk;
6546 struct tcpcheck_var *var = NULL;
6547 char *user = NULL, *errmsg = NULL;
6548 size_t packetlen = 0;
6549 int err_code = 0;
6550
6551 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6552 err_code |= ERR_WARN;
6553
6554 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
6555 goto out;
6556
Christopher Fauletce355072020-04-02 11:44:39 +02006557 curpx->options2 &= ~PR_O2_CHK_ANY;
6558 curpx->options2 |= PR_O2_TCPCHK_CHK;
6559
6560 free_tcpcheck_vars(&rules->preset_vars);
6561 rules->list = NULL;
6562 rules->flags = 0;
6563
6564 cur_arg += 2;
6565 if (!*args[cur_arg] || !*args[cur_arg+1]) {
6566 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
6567 file, line, args[0], args[1]);
6568 goto error;
6569 }
6570 if (strcmp(args[cur_arg], "user") == 0) {
6571 packetlen = 15 + strlen(args[cur_arg+1]);
6572 user = strdup(args[cur_arg+1]);
6573
Christopher Fauletb61caf42020-04-21 10:57:42 +02006574 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02006575 if (user == NULL || var == NULL) {
6576 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6577 goto error;
6578 }
6579 var->data.type = SMP_T_STR;
6580 var->data.u.str.area = user;
6581 var->data.u.str.data = strlen(user);
6582 LIST_INIT(&var->list);
6583 LIST_ADDQ(&rules->preset_vars, &var->list);
6584 user = NULL;
6585 var = NULL;
6586
Christopher Fauletb61caf42020-04-21 10:57:42 +02006587 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02006588 if (var == NULL) {
6589 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6590 goto error;
6591 }
6592 var->data.type = SMP_T_SINT;
6593 var->data.u.sint = packetlen;
6594 LIST_INIT(&var->list);
6595 LIST_ADDQ(&rules->preset_vars, &var->list);
6596 var = NULL;
6597 }
6598 else {
6599 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
6600 file, line, args[0], args[1]);
6601 goto error;
6602 }
6603
Christopher Faulet61cc8522020-04-20 14:54:42 +02006604 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006605 if (rs)
6606 goto ruleset_found;
6607
Christopher Faulet61cc8522020-04-20 14:54:42 +02006608 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02006609 if (rs == NULL) {
6610 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6611 goto error;
6612 }
6613
6614 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6615 1, curpx, &rs->rules, file, line, &errmsg);
6616 if (!chk) {
6617 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6618 goto error;
6619 }
6620 chk->index = 0;
6621 LIST_ADDQ(&rs->rules, &chk->list);
6622
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006623 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02006624 1, curpx, &rs->rules, file, line, &errmsg);
6625 if (!chk) {
6626 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6627 goto error;
6628 }
6629 chk->index = 1;
6630 LIST_ADDQ(&rs->rules, &chk->list);
6631
6632 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
6633 "min-recv", "5",
6634 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02006635 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02006636 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006637 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006638 if (!chk) {
6639 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6640 goto error;
6641 }
6642 chk->index = 2;
6643 LIST_ADDQ(&rs->rules, &chk->list);
6644
Christopher Fauletb841c742020-04-27 18:29:49 +02006645 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 +02006646 "min-recv", "9",
6647 "error-status", "L7STS",
6648 "on-success", "PostgreSQL server is ok",
6649 "on-error", "PostgreSQL unknown error",
6650 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006651 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02006652 if (!chk) {
6653 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6654 goto error;
6655 }
6656 chk->index = 3;
6657 LIST_ADDQ(&rs->rules, &chk->list);
6658
Christopher Fauletce355072020-04-02 11:44:39 +02006659 ruleset_found:
6660 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006661 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02006662
6663 out:
6664 free(errmsg);
6665 return err_code;
6666
6667 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006668 free(user);
6669 free(var);
6670 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006671 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006672 err_code |= ERR_ALERT | ERR_FATAL;
6673 goto out;
6674}
6675
6676
6677/* Parses the "option mysql-check" proxy keyword */
6678int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6679 const char *file, int line)
6680{
6681 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
6682 * const char mysql40_client_auth_pkt[] = {
6683 * "\x0e\x00\x00" // packet length
6684 * "\x01" // packet number
6685 * "\x00\x00" // client capabilities
6686 * "\x00\x00\x01" // max packet
6687 * "haproxy\x00" // username (null terminated string)
6688 * "\x00" // filler (always 0x00)
6689 * "\x01\x00\x00" // packet length
6690 * "\x00" // packet number
6691 * "\x01" // COM_QUIT command
6692 * };
6693 */
6694 static char mysql40_rsname[] = "*mysql40-check";
6695 static char mysql40_req[] = {
6696 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6697 "0080" /* client capabilities */
6698 "000001" /* max packet */
6699 "%[var(check.username),hex]00" /* the username */
6700 "00" /* filler (always 0x00) */
6701 "010000" /* packet length*/
6702 "00" /* sequence ID */
6703 "01" /* COM_QUIT command */
6704 };
6705
6706 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
6707 * const char mysql41_client_auth_pkt[] = {
6708 * "\x0e\x00\x00\" // packet length
6709 * "\x01" // packet number
6710 * "\x00\x00\x00\x00" // client capabilities
6711 * "\x00\x00\x00\x01" // max packet
6712 * "\x21" // character set (UTF-8)
6713 * char[23] // All zeroes
6714 * "haproxy\x00" // username (null terminated string)
6715 * "\x00" // filler (always 0x00)
6716 * "\x01\x00\x00" // packet length
6717 * "\x00" // packet number
6718 * "\x01" // COM_QUIT command
6719 * };
6720 */
6721 static char mysql41_rsname[] = "*mysql41-check";
6722 static char mysql41_req[] = {
6723 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
6724 "00820000" /* client capabilities */
6725 "00800001" /* max packet */
6726 "21" /* character set (UTF-8) */
6727 "000000000000000000000000" /* 23 bytes, al zeroes */
6728 "0000000000000000000000"
6729 "%[var(check.username),hex]00" /* the username */
6730 "00" /* filler (always 0x00) */
6731 "010000" /* packet length*/
6732 "00" /* sequence ID */
6733 "01" /* COM_QUIT command */
6734 };
6735
6736 struct tcpcheck_ruleset *rs = NULL;
6737 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6738 struct tcpcheck_rule *chk;
6739 struct tcpcheck_var *var = NULL;
6740 char *mysql_rsname = "*mysql-check";
6741 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
6742 int index = 0, err_code = 0;
6743
6744 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6745 err_code |= ERR_WARN;
6746
6747 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
6748 goto out;
6749
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006750 curpx->options2 &= ~PR_O2_CHK_ANY;
6751 curpx->options2 |= PR_O2_TCPCHK_CHK;
6752
6753 free_tcpcheck_vars(&rules->preset_vars);
6754 rules->list = NULL;
6755 rules->flags = 0;
6756
6757 cur_arg += 2;
6758 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006759 int packetlen, userlen;
6760
6761 if (strcmp(args[cur_arg], "user") != 0) {
6762 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
6763 file, line, args[0], args[1], args[cur_arg]);
6764 goto error;
6765 }
6766
6767 if (*(args[cur_arg+1]) == 0) {
6768 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
6769 file, line, args[0], args[1], args[cur_arg]);
6770 goto error;
6771 }
6772
6773 hdr = calloc(4, sizeof(*hdr));
6774 user = strdup(args[cur_arg+1]);
6775 userlen = strlen(args[cur_arg+1]);
6776
6777 if (hdr == NULL || user == NULL) {
6778 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6779 goto error;
6780 }
6781
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006782 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006783 packetlen = userlen + 7 + 27;
6784 mysql_req = mysql41_req;
6785 mysql_rsname = mysql41_rsname;
6786 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006787 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006788 packetlen = userlen + 7;
6789 mysql_req = mysql40_req;
6790 mysql_rsname = mysql40_rsname;
6791 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02006792 else {
6793 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
6794 file, line, args[cur_arg], args[cur_arg+2]);
6795 goto error;
6796 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006797
6798 hdr[0] = (unsigned char)(packetlen & 0xff);
6799 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
6800 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
6801 hdr[3] = 1;
6802
Christopher Fauletb61caf42020-04-21 10:57:42 +02006803 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006804 if (var == NULL) {
6805 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6806 goto error;
6807 }
6808 var->data.type = SMP_T_STR;
6809 var->data.u.str.area = hdr;
6810 var->data.u.str.data = 4;
6811 LIST_INIT(&var->list);
6812 LIST_ADDQ(&rules->preset_vars, &var->list);
6813 hdr = NULL;
6814 var = NULL;
6815
Christopher Fauletb61caf42020-04-21 10:57:42 +02006816 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006817 if (var == NULL) {
6818 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6819 goto error;
6820 }
6821 var->data.type = SMP_T_STR;
6822 var->data.u.str.area = user;
6823 var->data.u.str.data = strlen(user);
6824 LIST_INIT(&var->list);
6825 LIST_ADDQ(&rules->preset_vars, &var->list);
6826 user = NULL;
6827 var = NULL;
6828 }
6829
Christopher Faulet61cc8522020-04-20 14:54:42 +02006830 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006831 if (rs)
6832 goto ruleset_found;
6833
Christopher Faulet61cc8522020-04-20 14:54:42 +02006834 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006835 if (rs == NULL) {
6836 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6837 goto error;
6838 }
6839
6840 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
6841 1, curpx, &rs->rules, file, line, &errmsg);
6842 if (!chk) {
6843 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6844 goto error;
6845 }
6846 chk->index = index++;
6847 LIST_ADDQ(&rs->rules, &chk->list);
6848
6849 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02006850 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006851 1, curpx, &rs->rules, file, line, &errmsg);
6852 if (!chk) {
6853 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6854 goto error;
6855 }
6856 chk->index = index++;
6857 LIST_ADDQ(&rs->rules, &chk->list);
6858 }
6859
6860 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006861 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006862 if (!chk) {
6863 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6864 goto error;
6865 }
6866 chk->expect.custom = tcpcheck_mysql_expect_iniths;
6867 chk->index = index++;
6868 LIST_ADDQ(&rs->rules, &chk->list);
6869
6870 if (mysql_req) {
6871 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006872 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006873 if (!chk) {
6874 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6875 goto error;
6876 }
6877 chk->expect.custom = tcpcheck_mysql_expect_ok;
6878 chk->index = index++;
6879 LIST_ADDQ(&rs->rules, &chk->list);
6880 }
6881
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006882 ruleset_found:
6883 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006884 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02006885
6886 out:
6887 free(errmsg);
6888 return err_code;
6889
6890 error:
6891 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02006892 free(user);
6893 free(var);
6894 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02006895 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02006896 err_code |= ERR_ALERT | ERR_FATAL;
6897 goto out;
6898}
6899
Christopher Faulet1997eca2020-04-03 23:13:50 +02006900int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6901 const char *file, int line)
6902{
6903 static char *ldap_req = "300C020101600702010304008000";
6904
6905 struct tcpcheck_ruleset *rs = NULL;
6906 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6907 struct tcpcheck_rule *chk;
6908 char *errmsg = NULL;
6909 int err_code = 0;
6910
6911 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6912 err_code |= ERR_WARN;
6913
6914 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6915 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006916
6917 curpx->options2 &= ~PR_O2_CHK_ANY;
6918 curpx->options2 |= PR_O2_TCPCHK_CHK;
6919
6920 free_tcpcheck_vars(&rules->preset_vars);
6921 rules->list = NULL;
6922 rules->flags = 0;
6923
Christopher Faulet61cc8522020-04-20 14:54:42 +02006924 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006925 if (rs)
6926 goto ruleset_found;
6927
Christopher Faulet61cc8522020-04-20 14:54:42 +02006928 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02006929 if (rs == NULL) {
6930 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
6931 goto error;
6932 }
6933
6934 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
6935 1, curpx, &rs->rules, file, line, &errmsg);
6936 if (!chk) {
6937 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6938 goto error;
6939 }
6940 chk->index = 0;
6941 LIST_ADDQ(&rs->rules, &chk->list);
6942
6943 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
6944 "min-recv", "14",
6945 "on-error", "Not LDAPv3 protocol",
6946 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006947 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006948 if (!chk) {
6949 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6950 goto error;
6951 }
6952 chk->index = 1;
6953 LIST_ADDQ(&rs->rules, &chk->list);
6954
6955 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02006956 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02006957 if (!chk) {
6958 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
6959 goto error;
6960 }
6961 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
6962 chk->index = 2;
6963 LIST_ADDQ(&rs->rules, &chk->list);
6964
Christopher Faulet1997eca2020-04-03 23:13:50 +02006965 ruleset_found:
6966 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02006967 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02006968
6969 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02006970 free(errmsg);
6971 return err_code;
6972
6973 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02006974 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02006975 err_code |= ERR_ALERT | ERR_FATAL;
6976 goto out;
6977}
6978
6979int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
6980 const char *file, int line)
6981{
6982 struct tcpcheck_ruleset *rs = NULL;
6983 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
6984 struct tcpcheck_rule *chk;
6985 char *spop_req = NULL;
6986 char *errmsg = NULL;
6987 int spop_len = 0, err_code = 0;
6988
6989 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
6990 err_code |= ERR_WARN;
6991
6992 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
6993 goto out;
6994
Christopher Faulet267b01b2020-04-04 10:27:09 +02006995 curpx->options2 &= ~PR_O2_CHK_ANY;
6996 curpx->options2 |= PR_O2_TCPCHK_CHK;
6997
6998 free_tcpcheck_vars(&rules->preset_vars);
6999 rules->list = NULL;
7000 rules->flags = 0;
7001
7002
Christopher Faulet61cc8522020-04-20 14:54:42 +02007003 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007004 if (rs)
7005 goto ruleset_found;
7006
Christopher Faulet61cc8522020-04-20 14:54:42 +02007007 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02007008 if (rs == NULL) {
7009 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7010 goto error;
7011 }
7012
7013 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
7014 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7015 goto error;
7016 }
7017 chunk_reset(&trash);
7018 dump_binary(&trash, spop_req, spop_len);
7019 trash.area[trash.data] = '\0';
7020
7021 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
7022 1, curpx, &rs->rules, file, line, &errmsg);
7023 if (!chk) {
7024 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7025 goto error;
7026 }
7027 chk->index = 0;
7028 LIST_ADDQ(&rs->rules, &chk->list);
7029
7030 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007031 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02007032 if (!chk) {
7033 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
7034 goto error;
7035 }
7036 chk->expect.custom = tcpcheck_spop_expect_agenthello;
7037 chk->index = 1;
7038 LIST_ADDQ(&rs->rules, &chk->list);
7039
Christopher Faulet267b01b2020-04-04 10:27:09 +02007040 ruleset_found:
7041 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007042 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02007043
7044 out:
7045 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007046 free(errmsg);
7047 return err_code;
7048
7049 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007050 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02007051 err_code |= ERR_ALERT | ERR_FATAL;
7052 goto out;
7053}
Christopher Fauletce355072020-04-02 11:44:39 +02007054
Christopher Faulete5870d82020-04-15 11:32:03 +02007055
7056struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
7057{
7058 struct tcpcheck_rule *chk = NULL;
7059 struct tcpcheck_http_hdr *hdr = NULL;
7060 char *meth = NULL, *uri = NULL, *vsn = NULL;
7061 char *hdrs, *body;
7062
7063 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
7064 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
7065 if (hdrs == body)
7066 hdrs = NULL;
7067 if (hdrs) {
7068 *hdrs = '\0';
7069 hdrs +=2;
7070 }
7071 if (body) {
7072 *body = '\0';
7073 body += 4;
7074 }
7075 if (hdrs || body) {
7076 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
7077 " Please, consider to use 'http-check send' directive instead.");
7078 }
7079
7080 chk = calloc(1, sizeof(*chk));
7081 if (!chk) {
7082 memprintf(errmsg, "out of memory");
7083 goto error;
7084 }
7085 chk->action = TCPCHK_ACT_SEND;
7086 chk->send.type = TCPCHK_SEND_HTTP;
7087 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
7088 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
7089 LIST_INIT(&chk->send.http.hdrs);
7090
7091 /* Copy the method, uri and version */
7092 if (*args[cur_arg]) {
7093 if (!*args[cur_arg+1])
7094 uri = args[cur_arg];
7095 else
7096 meth = args[cur_arg];
7097 }
7098 if (*args[cur_arg+1])
7099 uri = args[cur_arg+1];
7100 if (*args[cur_arg+2])
7101 vsn = args[cur_arg+2];
7102
7103 if (meth) {
7104 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
7105 chk->send.http.meth.str.area = strdup(meth);
7106 chk->send.http.meth.str.data = strlen(meth);
7107 if (!chk->send.http.meth.str.area) {
7108 memprintf(errmsg, "out of memory");
7109 goto error;
7110 }
7111 }
7112 if (uri) {
7113 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007114 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007115 memprintf(errmsg, "out of memory");
7116 goto error;
7117 }
7118 }
7119 if (vsn) {
7120 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007121 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007122 memprintf(errmsg, "out of memory");
7123 goto error;
7124 }
7125 }
7126
7127 /* Copy the header */
7128 if (hdrs) {
7129 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
7130 struct h1m h1m;
7131 int i, ret;
7132
7133 /* Build and parse the request */
7134 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
7135
7136 h1m.flags = H1_MF_HDRS_ONLY;
7137 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
7138 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
7139 &h1m, NULL);
7140 if (ret <= 0) {
7141 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
7142 goto error;
7143 }
7144
Christopher Fauletb61caf42020-04-21 10:57:42 +02007145 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007146 hdr = calloc(1, sizeof(*hdr));
7147 if (!hdr) {
7148 memprintf(errmsg, "out of memory");
7149 goto error;
7150 }
7151 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007152 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02007153 if (!hdr->name.ptr) {
7154 memprintf(errmsg, "out of memory");
7155 goto error;
7156 }
7157
Christopher Fauletb61caf42020-04-21 10:57:42 +02007158 ist0(tmp_hdrs[i].v);
7159 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 +02007160 goto error;
7161 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
7162 }
7163 }
7164
7165 /* Copy the body */
7166 if (body) {
7167 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02007168 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02007169 memprintf(errmsg, "out of memory");
7170 goto error;
7171 }
7172 }
7173
7174 return chk;
7175
7176 error:
7177 free_tcpcheck_http_hdr(hdr);
7178 free_tcpcheck(chk, 0);
7179 return NULL;
7180}
7181
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007182int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7183 const char *file, int line)
7184{
Christopher Faulete5870d82020-04-15 11:32:03 +02007185 struct tcpcheck_ruleset *rs = NULL;
7186 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
7187 struct tcpcheck_rule *chk;
7188 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007189 int err_code = 0;
7190
7191 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
7192 err_code |= ERR_WARN;
7193
7194 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
7195 goto out;
7196
Christopher Faulete5870d82020-04-15 11:32:03 +02007197 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
7198 if (!chk) {
7199 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7200 goto error;
7201 }
7202 if (errmsg) {
7203 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
7204 err_code |= ERR_WARN;
7205 free(errmsg);
7206 errmsg = NULL;
7207 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007208
Christopher Faulete5870d82020-04-15 11:32:03 +02007209 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007210 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02007211 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007212
Christopher Faulete5870d82020-04-15 11:32:03 +02007213 free_tcpcheck_vars(&rules->preset_vars);
7214 rules->list = NULL;
7215 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007216
Christopher Faulete5870d82020-04-15 11:32:03 +02007217 /* Deduce the ruleset name from the proxy info */
7218 chunk_printf(&trash, "*http-check-%s_%s-%d",
7219 ((curpx == defpx) ? "defaults" : curpx->id),
7220 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007221
Christopher Faulet61cc8522020-04-20 14:54:42 +02007222 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007223 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02007224 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02007225 if (rs == NULL) {
7226 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
7227 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007228 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007229 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007230
Christopher Faulete5870d82020-04-15 11:32:03 +02007231 rules->list = &rs->rules;
7232 rules->flags |= TCPCHK_RULES_HTTP_CHK;
7233 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
7234 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
7235 rules->list = NULL;
7236 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007237 }
Christopher Faulete5870d82020-04-15 11:32:03 +02007238
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007239 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02007240 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007241 return err_code;
7242
7243 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02007244 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02007245 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02007246 err_code |= ERR_ALERT | ERR_FATAL;
7247 goto out;
7248}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007249
Christopher Faulet6f557912020-04-09 15:58:50 +02007250int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
7251 const char *file, int line)
7252{
7253 int err_code = 0;
7254
Christopher Faulet6f557912020-04-09 15:58:50 +02007255 curpx->options2 &= ~PR_O2_CHK_ANY;
7256 curpx->options2 |= PR_O2_EXT_CHK;
7257 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
7258 goto out;
7259
7260 out:
7261 return err_code;
7262}
7263
Christopher Fauletce8111e2020-04-06 15:04:11 +02007264/* Parse the "addr" server keyword */
7265static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7266 char **errmsg)
7267{
7268 struct sockaddr_storage *sk;
7269 struct protocol *proto;
7270 int port1, port2, err_code = 0;
7271
7272
7273 if (!*args[*cur_arg+1]) {
7274 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
7275 goto error;
7276 }
7277
7278 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
7279 if (!sk) {
7280 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
7281 goto error;
7282 }
7283
7284 proto = protocol_by_family(sk->ss_family);
7285 if (!proto || !proto->connect) {
7286 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
7287 args[*cur_arg], args[*cur_arg+1]);
7288 goto error;
7289 }
7290
7291 if (port1 != port2) {
7292 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
7293 args[*cur_arg], args[*cur_arg+1]);
7294 goto error;
7295 }
7296
7297 srv->check.addr = srv->agent.addr = *sk;
7298 srv->flags |= SRV_F_CHECKADDR;
7299 srv->flags |= SRV_F_AGENTADDR;
7300
7301 out:
7302 return err_code;
7303
7304 error:
7305 err_code |= ERR_ALERT | ERR_FATAL;
7306 goto out;
7307}
7308
7309
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007310/* Parse the "agent-addr" server keyword */
7311static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7312 char **errmsg)
7313{
7314 int err_code = 0;
7315
7316 if (!*(args[*cur_arg+1])) {
7317 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
7318 goto error;
7319 }
7320 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
7321 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
7322 goto error;
7323 }
7324
7325 out:
7326 return err_code;
7327
7328 error:
7329 err_code |= ERR_ALERT | ERR_FATAL;
7330 goto out;
7331}
7332
7333/* Parse the "agent-check" server keyword */
7334static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7335 char **errmsg)
7336{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007337 struct tcpcheck_ruleset *rs = NULL;
7338 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7339 struct tcpcheck_rule *chk;
7340 int err_code = 0;
7341
7342 if (srv->do_agent)
7343 goto out;
7344
7345 if (!rules) {
7346 rules = calloc(1, sizeof(*rules));
7347 if (!rules) {
7348 memprintf(errmsg, "out of memory.");
7349 goto error;
7350 }
7351 LIST_INIT(&rules->preset_vars);
7352 srv->agent.tcpcheck_rules = rules;
7353 }
7354 rules->list = NULL;
7355 rules->flags = 0;
7356
Christopher Faulet61cc8522020-04-20 14:54:42 +02007357 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007358 if (rs)
7359 goto ruleset_found;
7360
Christopher Faulet61cc8522020-04-20 14:54:42 +02007361 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007362 if (rs == NULL) {
7363 memprintf(errmsg, "out of memory.");
7364 goto error;
7365 }
7366
Christopher Fauletb50b3e62020-05-05 18:43:43 +02007367 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007368 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
7369 if (!chk) {
7370 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7371 goto error;
7372 }
7373 chk->index = 0;
7374 LIST_ADDQ(&rs->rules, &chk->list);
7375
7376 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02007377 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
7378 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007379 if (!chk) {
7380 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
7381 goto error;
7382 }
7383 chk->expect.custom = tcpcheck_agent_expect_reply;
7384 chk->index = 1;
7385 LIST_ADDQ(&rs->rules, &chk->list);
7386
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007387 ruleset_found:
7388 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02007389 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007390 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007391
7392 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007393 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007394
7395 error:
7396 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02007397 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007398 err_code |= ERR_ALERT | ERR_FATAL;
7399 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007400}
7401
7402/* Parse the "agent-inter" server keyword */
7403static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7404 char **errmsg)
7405{
7406 const char *err = NULL;
7407 unsigned int delay;
7408 int err_code = 0;
7409
7410 if (!*(args[*cur_arg+1])) {
7411 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7412 goto error;
7413 }
7414
7415 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7416 if (err == PARSE_TIME_OVER) {
7417 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7418 args[*cur_arg+1], args[*cur_arg], srv->id);
7419 goto error;
7420 }
7421 else if (err == PARSE_TIME_UNDER) {
7422 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7423 args[*cur_arg+1], args[*cur_arg], srv->id);
7424 goto error;
7425 }
7426 else if (err) {
7427 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7428 *err, srv->id);
7429 goto error;
7430 }
7431 if (delay <= 0) {
7432 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7433 delay, args[*cur_arg], srv->id);
7434 goto error;
7435 }
7436 srv->agent.inter = delay;
7437
7438 out:
7439 return err_code;
7440
7441 error:
7442 err_code |= ERR_ALERT | ERR_FATAL;
7443 goto out;
7444}
7445
7446/* Parse the "agent-port" server keyword */
7447static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7448 char **errmsg)
7449{
7450 int err_code = 0;
7451
7452 if (!*(args[*cur_arg+1])) {
7453 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7454 goto error;
7455 }
7456
7457 global.maxsock++;
7458 srv->agent.port = atol(args[*cur_arg+1]);
7459
7460 out:
7461 return err_code;
7462
7463 error:
7464 err_code |= ERR_ALERT | ERR_FATAL;
7465 goto out;
7466}
7467
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007468int set_srv_agent_send(struct server *srv, const char *send)
7469{
7470 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
7471 struct tcpcheck_var *var = NULL;
7472 char *str;
7473
7474 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02007475 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007476 if (str == NULL || var == NULL)
7477 goto error;
7478
7479 free_tcpcheck_vars(&rules->preset_vars);
7480
7481 var->data.type = SMP_T_STR;
7482 var->data.u.str.area = str;
7483 var->data.u.str.data = strlen(str);
7484 LIST_INIT(&var->list);
7485 LIST_ADDQ(&rules->preset_vars, &var->list);
7486
7487 return 1;
7488
7489 error:
7490 free(str);
7491 free(var);
7492 return 0;
7493}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007494
7495/* Parse the "agent-send" server keyword */
7496static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7497 char **errmsg)
7498{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007499 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007500 int err_code = 0;
7501
7502 if (!*(args[*cur_arg+1])) {
7503 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
7504 goto error;
7505 }
7506
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007507 if (!rules) {
7508 rules = calloc(1, sizeof(*rules));
7509 if (!rules) {
7510 memprintf(errmsg, "out of memory.");
7511 goto error;
7512 }
7513 LIST_INIT(&rules->preset_vars);
7514 srv->agent.tcpcheck_rules = rules;
7515 }
7516
7517 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007518 memprintf(errmsg, "out of memory.");
7519 goto error;
7520 }
7521
7522 out:
7523 return err_code;
7524
7525 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007526 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007527 err_code |= ERR_ALERT | ERR_FATAL;
7528 goto out;
7529}
7530
7531/* Parse the "no-agent-send" server keyword */
7532static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7533 char **errmsg)
7534{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02007535 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007536 return 0;
7537}
7538
Christopher Fauletce8111e2020-04-06 15:04:11 +02007539/* Parse the "check" server keyword */
7540static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7541 char **errmsg)
7542{
7543 srv->do_check = 1;
7544 return 0;
7545}
7546
7547/* Parse the "check-send-proxy" server keyword */
7548static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7549 char **errmsg)
7550{
7551 srv->check.send_proxy = 1;
7552 return 0;
7553}
7554
7555/* Parse the "check-via-socks4" server keyword */
7556static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7557 char **errmsg)
7558{
7559 srv->check.via_socks4 = 1;
7560 return 0;
7561}
7562
7563/* Parse the "no-check" server keyword */
7564static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7565 char **errmsg)
7566{
7567 deinit_srv_check(srv);
7568 return 0;
7569}
7570
7571/* Parse the "no-check-send-proxy" server keyword */
7572static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7573 char **errmsg)
7574{
7575 srv->check.send_proxy = 0;
7576 return 0;
7577}
7578
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007579/* parse the "check-proto" server keyword */
7580static int srv_parse_check_proto(char **args, int *cur_arg,
7581 struct proxy *px, struct server *newsrv, char **err)
7582{
7583 int err_code = 0;
7584
7585 if (!*args[*cur_arg + 1]) {
7586 memprintf(err, "'%s' : missing value", args[*cur_arg]);
7587 goto error;
7588 }
7589 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
7590 if (!newsrv->check.mux_proto) {
7591 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
7592 goto error;
7593 }
7594
7595 out:
7596 return err_code;
7597
7598 error:
7599 err_code |= ERR_ALERT | ERR_FATAL;
7600 goto out;
7601}
7602
7603
Christopher Fauletce8111e2020-04-06 15:04:11 +02007604/* Parse the "rise" server keyword */
7605static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7606 char **errmsg)
7607{
7608 int err_code = 0;
7609
7610 if (!*args[*cur_arg + 1]) {
7611 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7612 goto error;
7613 }
7614
7615 srv->check.rise = atol(args[*cur_arg+1]);
7616 if (srv->check.rise <= 0) {
7617 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7618 goto error;
7619 }
7620
7621 if (srv->check.health)
7622 srv->check.health = srv->check.rise;
7623
7624 out:
7625 return err_code;
7626
7627 error:
7628 deinit_srv_agent_check(srv);
7629 err_code |= ERR_ALERT | ERR_FATAL;
7630 goto out;
7631 return 0;
7632}
7633
7634/* Parse the "fall" server keyword */
7635static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7636 char **errmsg)
7637{
7638 int err_code = 0;
7639
7640 if (!*args[*cur_arg + 1]) {
7641 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
7642 goto error;
7643 }
7644
7645 srv->check.fall = atol(args[*cur_arg+1]);
7646 if (srv->check.fall <= 0) {
7647 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
7648 goto error;
7649 }
7650
7651 out:
7652 return err_code;
7653
7654 error:
7655 deinit_srv_agent_check(srv);
7656 err_code |= ERR_ALERT | ERR_FATAL;
7657 goto out;
7658 return 0;
7659}
7660
7661/* Parse the "inter" server keyword */
7662static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7663 char **errmsg)
7664{
7665 const char *err = NULL;
7666 unsigned int delay;
7667 int err_code = 0;
7668
7669 if (!*(args[*cur_arg+1])) {
7670 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7671 goto error;
7672 }
7673
7674 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7675 if (err == PARSE_TIME_OVER) {
7676 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7677 args[*cur_arg+1], args[*cur_arg], srv->id);
7678 goto error;
7679 }
7680 else if (err == PARSE_TIME_UNDER) {
7681 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7682 args[*cur_arg+1], args[*cur_arg], srv->id);
7683 goto error;
7684 }
7685 else if (err) {
7686 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7687 *err, srv->id);
7688 goto error;
7689 }
7690 if (delay <= 0) {
7691 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7692 delay, args[*cur_arg], srv->id);
7693 goto error;
7694 }
7695 srv->check.inter = delay;
7696
7697 out:
7698 return err_code;
7699
7700 error:
7701 err_code |= ERR_ALERT | ERR_FATAL;
7702 goto out;
7703}
7704
7705
7706/* Parse the "fastinter" server keyword */
7707static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7708 char **errmsg)
7709{
7710 const char *err = NULL;
7711 unsigned int delay;
7712 int err_code = 0;
7713
7714 if (!*(args[*cur_arg+1])) {
7715 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7716 goto error;
7717 }
7718
7719 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7720 if (err == PARSE_TIME_OVER) {
7721 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7722 args[*cur_arg+1], args[*cur_arg], srv->id);
7723 goto error;
7724 }
7725 else if (err == PARSE_TIME_UNDER) {
7726 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7727 args[*cur_arg+1], args[*cur_arg], srv->id);
7728 goto error;
7729 }
7730 else if (err) {
7731 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7732 *err, srv->id);
7733 goto error;
7734 }
7735 if (delay <= 0) {
7736 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7737 delay, args[*cur_arg], srv->id);
7738 goto error;
7739 }
7740 srv->check.fastinter = delay;
7741
7742 out:
7743 return err_code;
7744
7745 error:
7746 err_code |= ERR_ALERT | ERR_FATAL;
7747 goto out;
7748}
7749
7750
7751/* Parse the "downinter" server keyword */
7752static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7753 char **errmsg)
7754{
7755 const char *err = NULL;
7756 unsigned int delay;
7757 int err_code = 0;
7758
7759 if (!*(args[*cur_arg+1])) {
7760 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
7761 goto error;
7762 }
7763
7764 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
7765 if (err == PARSE_TIME_OVER) {
7766 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
7767 args[*cur_arg+1], args[*cur_arg], srv->id);
7768 goto error;
7769 }
7770 else if (err == PARSE_TIME_UNDER) {
7771 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
7772 args[*cur_arg+1], args[*cur_arg], srv->id);
7773 goto error;
7774 }
7775 else if (err) {
7776 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
7777 *err, srv->id);
7778 goto error;
7779 }
7780 if (delay <= 0) {
7781 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
7782 delay, args[*cur_arg], srv->id);
7783 goto error;
7784 }
7785 srv->check.downinter = delay;
7786
7787 out:
7788 return err_code;
7789
7790 error:
7791 err_code |= ERR_ALERT | ERR_FATAL;
7792 goto out;
7793}
7794
7795/* Parse the "port" server keyword */
7796static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
7797 char **errmsg)
7798{
7799 int err_code = 0;
7800
7801 if (!*(args[*cur_arg+1])) {
7802 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
7803 goto error;
7804 }
7805
7806 global.maxsock++;
7807 srv->check.port = atol(args[*cur_arg+1]);
7808 srv->flags |= SRV_F_CHECKPORT;
7809
7810 out:
7811 return err_code;
7812
7813 error:
7814 err_code |= ERR_ALERT | ERR_FATAL;
7815 goto out;
7816}
7817
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007818static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02007819 { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
7820 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
7821 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007822 { 0, NULL, NULL },
7823}};
7824
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007825static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02007826 { "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 +02007827 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
7828 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
7829 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
7830 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
7831 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007832 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02007833 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007834 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
7835 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007836 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02007837 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
7838 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
7839 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
7840 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
7841 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
7842 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
7843 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
7844 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007845 { NULL, NULL, 0 },
7846}};
7847
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007848INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02007849INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01007850
Willy Tarreaubd741542010-03-16 18:46:54 +01007851/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02007852 * Local variables:
7853 * c-indent-level: 8
7854 * c-basic-offset: 8
7855 * End:
7856 */