mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-11-29 14:50:59 +01:00
MEDIUM: checks: Implement MySQL check using tcp-check rules
A share tcp-check ruleset is now created to support MySQL checks. This way no
extra memory is used if several backends use a MySQL check.
One for the following sequence is used :
## If no extra params are set
tcp-check connect default linger
tcp-check expect custom ## will test the initial handshake
## If the username is defined
tcp-check connect default linger
tcp-check send-binary MYSQL_REQ log-format
tcp-check expect custom ## will test the initial handshake
tcp-check expect custom ## will test the reply to the client message
The log-format hexa string MYSQL_REQ depends on 2 preset variables, the packet
header containing the packet length and the sequence ID (check.header) and the
username (check.username). If is also different if the "post-41" option is set
or not. Expect rules relies on custom functions to check MySQL server packets.
This commit is contained in:
parent
ce355074f1
commit
f2b3be5c27
@ -77,6 +77,8 @@ int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struc
|
|||||||
const char *file, int line);
|
const char *file, int line);
|
||||||
int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
||||||
const char *file, int line);
|
const char *file, int line);
|
||||||
|
int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
||||||
|
const char *file, int line);
|
||||||
|
|
||||||
#endif /* _PROTO_CHECKS_H */
|
#endif /* _PROTO_CHECKS_H */
|
||||||
|
|
||||||
|
|||||||
@ -315,6 +315,7 @@ struct tcpcheck_rule {
|
|||||||
#define TCPCHK_RULES_PGSQL_CHK 0x00000010
|
#define TCPCHK_RULES_PGSQL_CHK 0x00000010
|
||||||
#define TCPCHK_RULES_REDIS_CHK 0x00000020
|
#define TCPCHK_RULES_REDIS_CHK 0x00000020
|
||||||
#define TCPCHK_RULES_SMTP_CHK 0x00000030
|
#define TCPCHK_RULES_SMTP_CHK 0x00000030
|
||||||
|
#define TCPCHK_RULES_MYSQL_CHK 0x00000050
|
||||||
#define TCPCHK_RULES_SSL3_CHK 0x00000070
|
#define TCPCHK_RULES_SSL3_CHK 0x00000070
|
||||||
|
|
||||||
/* A list of tcp-check vars, to be registered before executing a ruleset */
|
/* A list of tcp-check vars, to be registered before executing a ruleset */
|
||||||
|
|||||||
@ -171,7 +171,7 @@ enum PR_SRV_STATE_FILE {
|
|||||||
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
|
||||||
/* unused: 0x10000000..0x30000000 */
|
/* unused: 0x10000000..0x30000000 */
|
||||||
#define PR_O2_HTTP_CHK 0x40000000 /* use HTTP 'OPTIONS' method to check server health */
|
#define PR_O2_HTTP_CHK 0x40000000 /* use HTTP 'OPTIONS' method to check server health */
|
||||||
#define PR_O2_MYSQL_CHK 0x50000000 /* use MYSQL check for server health */
|
/* unused 0x50000000 */
|
||||||
#define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */
|
#define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */
|
||||||
/* unused: 0x70000000 */
|
/* unused: 0x70000000 */
|
||||||
#define PR_O2_LB_AGENT_CHK 0x80000000 /* use a TCP connection to obtain a metric of server health */
|
#define PR_O2_LB_AGENT_CHK 0x80000000 /* use a TCP connection to obtain a metric of server health */
|
||||||
|
|||||||
@ -2416,123 +2416,10 @@ stats_error_parsing:
|
|||||||
if (err_code & ERR_FATAL)
|
if (err_code & ERR_FATAL)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (!strcmp(args[1], "mysql-check")) {
|
else if (!strcmp(args[1], "mysql-check")) {
|
||||||
/* use MYSQL request to check servers' health */
|
err_code |= proxy_parse_mysql_check_opt(args, 0, curproxy, &defproxy, file, linenum);
|
||||||
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL))
|
if (err_code & ERR_FATAL)
|
||||||
err_code |= ERR_WARN;
|
goto out;
|
||||||
|
|
||||||
free(curproxy->check_req);
|
|
||||||
curproxy->check_req = NULL;
|
|
||||||
curproxy->options2 &= ~PR_O2_CHK_ANY;
|
|
||||||
curproxy->options2 |= PR_O2_MYSQL_CHK;
|
|
||||||
|
|
||||||
/* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
|
|
||||||
* const char mysql40_client_auth_pkt[] = {
|
|
||||||
* "\x0e\x00\x00" // packet length
|
|
||||||
* "\x01" // packet number
|
|
||||||
* "\x00\x00" // client capabilities
|
|
||||||
* "\x00\x00\x01" // max packet
|
|
||||||
* "haproxy\x00" // username (null terminated string)
|
|
||||||
* "\x00" // filler (always 0x00)
|
|
||||||
* "\x01\x00\x00" // packet length
|
|
||||||
* "\x00" // packet number
|
|
||||||
* "\x01" // COM_QUIT command
|
|
||||||
* };
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
|
|
||||||
* const char mysql41_client_auth_pkt[] = {
|
|
||||||
* "\x0e\x00\x00\" // packet length
|
|
||||||
* "\x01" // packet number
|
|
||||||
* "\x00\x00\x00\x00" // client capabilities
|
|
||||||
* "\x00\x00\x00\x01" // max packet
|
|
||||||
* "\x21" // character set (UTF-8)
|
|
||||||
* char[23] // All zeroes
|
|
||||||
* "haproxy\x00" // username (null terminated string)
|
|
||||||
* "\x00" // filler (always 0x00)
|
|
||||||
* "\x01\x00\x00" // packet length
|
|
||||||
* "\x00" // packet number
|
|
||||||
* "\x01" // COM_QUIT command
|
|
||||||
* };
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
if (*(args[2])) {
|
|
||||||
int cur_arg = 2;
|
|
||||||
|
|
||||||
while (*(args[cur_arg])) {
|
|
||||||
if (strcmp(args[cur_arg], "user") == 0) {
|
|
||||||
char *mysqluser;
|
|
||||||
int packetlen, reqlen, userlen;
|
|
||||||
|
|
||||||
/* suboption header - needs additional argument for it */
|
|
||||||
if (*(args[cur_arg+1]) == 0) {
|
|
||||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
|
|
||||||
file, linenum, args[0], args[1], args[cur_arg]);
|
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
mysqluser = args[cur_arg + 1];
|
|
||||||
userlen = strlen(mysqluser);
|
|
||||||
|
|
||||||
if (*(args[cur_arg+2])) {
|
|
||||||
if (!strcmp(args[cur_arg+2], "post-41")) {
|
|
||||||
packetlen = userlen + 7 + 27;
|
|
||||||
reqlen = packetlen + 9;
|
|
||||||
|
|
||||||
free(curproxy->check_req);
|
|
||||||
curproxy->check_req = calloc(1, reqlen);
|
|
||||||
curproxy->check_len = reqlen;
|
|
||||||
|
|
||||||
snprintf(curproxy->check_req, 4, "%c%c%c",
|
|
||||||
((unsigned char) packetlen & 0xff),
|
|
||||||
((unsigned char) (packetlen >> 8) & 0xff),
|
|
||||||
((unsigned char) (packetlen >> 16) & 0xff));
|
|
||||||
|
|
||||||
curproxy->check_req[3] = 1;
|
|
||||||
curproxy->check_req[5] = 0x82; // 130
|
|
||||||
curproxy->check_req[11] = 1;
|
|
||||||
curproxy->check_req[12] = 33;
|
|
||||||
memcpy(&curproxy->check_req[36], mysqluser, userlen);
|
|
||||||
curproxy->check_req[36 + userlen + 1 + 1] = 1;
|
|
||||||
curproxy->check_req[36 + userlen + 1 + 1 + 4] = 1;
|
|
||||||
cur_arg += 3;
|
|
||||||
} else {
|
|
||||||
ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41'.\n", file, linenum, args[cur_arg+2]);
|
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
packetlen = userlen + 7;
|
|
||||||
reqlen = packetlen + 9;
|
|
||||||
|
|
||||||
free(curproxy->check_req);
|
|
||||||
curproxy->check_req = calloc(1, reqlen);
|
|
||||||
curproxy->check_len = reqlen;
|
|
||||||
|
|
||||||
snprintf(curproxy->check_req, 4, "%c%c%c",
|
|
||||||
((unsigned char) packetlen & 0xff),
|
|
||||||
((unsigned char) (packetlen >> 8) & 0xff),
|
|
||||||
((unsigned char) (packetlen >> 16) & 0xff));
|
|
||||||
|
|
||||||
curproxy->check_req[3] = 1;
|
|
||||||
curproxy->check_req[5] = 0x80;
|
|
||||||
curproxy->check_req[8] = 1;
|
|
||||||
memcpy(&curproxy->check_req[9], mysqluser, userlen);
|
|
||||||
curproxy->check_req[9 + userlen + 1 + 1] = 1;
|
|
||||||
curproxy->check_req[9 + userlen + 1 + 1 + 4] = 1;
|
|
||||||
cur_arg += 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* unknown suboption - catchall */
|
|
||||||
ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
|
|
||||||
file, linenum, args[0], args[1]);
|
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
} /* end while loop */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(args[1], "ldap-check")) {
|
else if (!strcmp(args[1], "ldap-check")) {
|
||||||
/* use LDAP request to check servers' health */
|
/* use LDAP request to check servers' health */
|
||||||
|
|||||||
417
src/checks.c
417
src/checks.c
@ -1156,100 +1156,6 @@ static void __event_srv_chk_r(struct conn_stream *cs)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PR_O2_MYSQL_CHK:
|
|
||||||
if (!done && b_data(&check->bi) < 5)
|
|
||||||
goto wait_more_data;
|
|
||||||
|
|
||||||
/* do not reset when closing, servers don't like this */
|
|
||||||
if (conn_ctrl_ready(cs->conn))
|
|
||||||
fdtab[cs->conn->handle.fd].linger_risk = 0;
|
|
||||||
|
|
||||||
if (s->proxy->check_len == 0) { // old mode
|
|
||||||
if (*(b_head(&check->bi) + 4) != '\xff') {
|
|
||||||
/* We set the MySQL Version in description for information purpose
|
|
||||||
* FIXME : it can be cool to use MySQL Version for other purpose,
|
|
||||||
* like mark as down old MySQL server.
|
|
||||||
*/
|
|
||||||
if (b_data(&check->bi) > 51) {
|
|
||||||
desc = ltrim(b_head(&check->bi) + 5, ' ');
|
|
||||||
set_server_check_status(check, HCHK_STATUS_L7OKD, desc);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!done)
|
|
||||||
goto wait_more_data;
|
|
||||||
|
|
||||||
/* it seems we have a OK packet but without a valid length,
|
|
||||||
* it must be a protocol error
|
|
||||||
*/
|
|
||||||
set_server_check_status(check, HCHK_STATUS_L7RSP, b_head(&check->bi));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* An error message is attached in the Error packet */
|
|
||||||
desc = ltrim(b_head(&check->bi) + 7, ' ');
|
|
||||||
set_server_check_status(check, HCHK_STATUS_L7STS, desc);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsigned int first_packet_len = ((unsigned int) *b_head(&check->bi)) +
|
|
||||||
(((unsigned int) *(b_head(&check->bi) + 1)) << 8) +
|
|
||||||
(((unsigned int) *(b_head(&check->bi) + 2)) << 16);
|
|
||||||
|
|
||||||
if (b_data(&check->bi) == first_packet_len + 4) {
|
|
||||||
/* MySQL Error packet always begin with field_count = 0xff */
|
|
||||||
if (*(b_head(&check->bi) + 4) != '\xff') {
|
|
||||||
/* We have only one MySQL packet and it is a Handshake Initialization packet
|
|
||||||
* but we need to have a second packet to know if it is alright
|
|
||||||
*/
|
|
||||||
if (!done && b_data(&check->bi) < first_packet_len + 5)
|
|
||||||
goto wait_more_data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* We have only one packet and it is an Error packet,
|
|
||||||
* an error message is attached, so we can display it
|
|
||||||
*/
|
|
||||||
desc = &b_head(&check->bi)[7];
|
|
||||||
//ha_warning("onlyoneERR: %s\n", desc);
|
|
||||||
set_server_check_status(check, HCHK_STATUS_L7STS, desc);
|
|
||||||
}
|
|
||||||
} else if (b_data(&check->bi) > first_packet_len + 4) {
|
|
||||||
unsigned int second_packet_len = ((unsigned int) *(b_head(&check->bi) + first_packet_len + 4)) +
|
|
||||||
(((unsigned int) *(b_head(&check->bi) + first_packet_len + 5)) << 8) +
|
|
||||||
(((unsigned int) *(b_head(&check->bi) + first_packet_len + 6)) << 16);
|
|
||||||
|
|
||||||
if (b_data(&check->bi) == first_packet_len + 4 + second_packet_len + 4 ) {
|
|
||||||
/* We have 2 packets and that's good */
|
|
||||||
/* Check if the second packet is a MySQL Error packet or not */
|
|
||||||
if (*(b_head(&check->bi) + first_packet_len + 8) != '\xff') {
|
|
||||||
/* No error packet */
|
|
||||||
/* We set the MySQL Version in description for information purpose */
|
|
||||||
desc = &b_head(&check->bi)[5];
|
|
||||||
//ha_warning("2packetOK: %s\n", desc);
|
|
||||||
set_server_check_status(check, HCHK_STATUS_L7OKD, desc);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* An error message is attached in the Error packet
|
|
||||||
* so we can display it ! :)
|
|
||||||
*/
|
|
||||||
desc = &b_head(&check->bi)[first_packet_len+11];
|
|
||||||
//ha_warning("2packetERR: %s\n", desc);
|
|
||||||
set_server_check_status(check, HCHK_STATUS_L7STS, desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!done)
|
|
||||||
goto wait_more_data;
|
|
||||||
|
|
||||||
/* it seems we have a Handshake Initialization packet but without a valid length,
|
|
||||||
* it must be a protocol error
|
|
||||||
*/
|
|
||||||
desc = &b_head(&check->bi)[5];
|
|
||||||
//ha_warning("protoerr: %s\n", desc);
|
|
||||||
set_server_check_status(check, HCHK_STATUS_L7RSP, desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PR_O2_LDAP_CHK:
|
case PR_O2_LDAP_CHK:
|
||||||
if (!done && b_data(&check->bi) < 14)
|
if (!done && b_data(&check->bi) < 14)
|
||||||
goto wait_more_data;
|
goto wait_more_data;
|
||||||
@ -2776,6 +2682,95 @@ static void tcpcheck_onsuccess_message(struct buffer *msg, struct check *check,
|
|||||||
*(b_tail(msg)) = '\0';
|
*(b_tail(msg)) = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum tcpcheck_eval_ret tcpcheck_mysql_expect_packet(struct check *check, struct tcpcheck_rule *rule,
|
||||||
|
unsigned int offset, int last_read)
|
||||||
|
{
|
||||||
|
enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
|
||||||
|
enum healthcheck_status status;
|
||||||
|
struct buffer *msg = NULL;
|
||||||
|
struct ist desc = ist(NULL);
|
||||||
|
unsigned int err = 0, plen = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* 3 Bytes for the packet length and 1 byte for the sequence id */
|
||||||
|
if (!last_read && b_data(&check->bi) < offset+4) {
|
||||||
|
if (!last_read)
|
||||||
|
goto wait_more_data;
|
||||||
|
|
||||||
|
/* invalid length or truncated response */
|
||||||
|
status = HCHK_STATUS_L7RSP;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
plen = ((unsigned char) *b_peek(&check->bi, offset)) +
|
||||||
|
(((unsigned char) *(b_peek(&check->bi, offset+1))) << 8) +
|
||||||
|
(((unsigned char) *(b_peek(&check->bi, offset+2))) << 16);
|
||||||
|
|
||||||
|
if (b_data(&check->bi) < offset+plen+4) {
|
||||||
|
if (!last_read)
|
||||||
|
goto wait_more_data;
|
||||||
|
|
||||||
|
/* invalid length or truncated response */
|
||||||
|
status = HCHK_STATUS_L7RSP;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*b_peek(&check->bi, offset+4) == '\xff') {
|
||||||
|
/* MySQL Error packet always begin with field_count = 0xff */
|
||||||
|
status = HCHK_STATUS_L7STS;
|
||||||
|
err = ((unsigned char) *b_peek(&check->bi, offset+5)) +
|
||||||
|
(((unsigned char) *(b_peek(&check->bi, offset+6))) << 8);
|
||||||
|
desc = ist2(b_peek(&check->bi, offset+7), b_data(&check->bi) - offset - 7);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_next_tcpcheck_rule(check->tcpcheck_rules, rule) != NULL) {
|
||||||
|
/* Not the last rule, continue */
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We set the MySQL Version in description for information purpose
|
||||||
|
* FIXME : it can be cool to use MySQL Version for other purpose,
|
||||||
|
* like mark as down old MySQL server.
|
||||||
|
*/
|
||||||
|
set_server_check_status(check, HCHK_STATUS_L7OKD, b_peek(&check->bi, 5));
|
||||||
|
|
||||||
|
out:
|
||||||
|
free_trash_chunk(msg);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
error:
|
||||||
|
ret = TCPCHK_EVAL_STOP;
|
||||||
|
check->code = err;
|
||||||
|
msg = alloc_trash_chunk();
|
||||||
|
if (msg)
|
||||||
|
tcpcheck_onerror_message(msg, check, rule, 0, desc);
|
||||||
|
set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
wait_more_data:
|
||||||
|
ret = TCPCHK_EVAL_WAIT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum tcpcheck_eval_ret tcpcheck_mysql_expect_iniths(struct check *check, struct tcpcheck_rule *rule, int last_read)
|
||||||
|
{
|
||||||
|
return tcpcheck_mysql_expect_packet(check, rule, 0, last_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, struct tcpcheck_rule *rule, int last_read)
|
||||||
|
{
|
||||||
|
unsigned int hslen = 0;
|
||||||
|
|
||||||
|
hslen = 4 + ((unsigned char) *b_head(&check->bi)) +
|
||||||
|
(((unsigned char) *(b_peek(&check->bi, 1))) << 8) +
|
||||||
|
(((unsigned char) *(b_peek(&check->bi, 2))) << 16);
|
||||||
|
|
||||||
|
return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Evaluate a TCPCHK_ACT_CONNECT rule. It returns 1 to evaluate the next rule, 0
|
/* Evaluate a TCPCHK_ACT_CONNECT rule. It returns 1 to evaluate the next rule, 0
|
||||||
* to wait and -1 to stop the check. */
|
* to wait and -1 to stop the check. */
|
||||||
static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
|
static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
|
||||||
@ -5535,6 +5530,240 @@ int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parses the "option mysql-check" proxy keyword */
|
||||||
|
int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
||||||
|
const char *file, int line)
|
||||||
|
{
|
||||||
|
/* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
|
||||||
|
* const char mysql40_client_auth_pkt[] = {
|
||||||
|
* "\x0e\x00\x00" // packet length
|
||||||
|
* "\x01" // packet number
|
||||||
|
* "\x00\x00" // client capabilities
|
||||||
|
* "\x00\x00\x01" // max packet
|
||||||
|
* "haproxy\x00" // username (null terminated string)
|
||||||
|
* "\x00" // filler (always 0x00)
|
||||||
|
* "\x01\x00\x00" // packet length
|
||||||
|
* "\x00" // packet number
|
||||||
|
* "\x01" // COM_QUIT command
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
static char mysql40_rsname[] = "*mysql40-check";
|
||||||
|
static char mysql40_req[] = {
|
||||||
|
"%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
|
||||||
|
"0080" /* client capabilities */
|
||||||
|
"000001" /* max packet */
|
||||||
|
"%[var(check.username),hex]00" /* the username */
|
||||||
|
"00" /* filler (always 0x00) */
|
||||||
|
"010000" /* packet length*/
|
||||||
|
"00" /* sequence ID */
|
||||||
|
"01" /* COM_QUIT command */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
|
||||||
|
* const char mysql41_client_auth_pkt[] = {
|
||||||
|
* "\x0e\x00\x00\" // packet length
|
||||||
|
* "\x01" // packet number
|
||||||
|
* "\x00\x00\x00\x00" // client capabilities
|
||||||
|
* "\x00\x00\x00\x01" // max packet
|
||||||
|
* "\x21" // character set (UTF-8)
|
||||||
|
* char[23] // All zeroes
|
||||||
|
* "haproxy\x00" // username (null terminated string)
|
||||||
|
* "\x00" // filler (always 0x00)
|
||||||
|
* "\x01\x00\x00" // packet length
|
||||||
|
* "\x00" // packet number
|
||||||
|
* "\x01" // COM_QUIT command
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
static char mysql41_rsname[] = "*mysql41-check";
|
||||||
|
static char mysql41_req[] = {
|
||||||
|
"%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
|
||||||
|
"00820000" /* client capabilities */
|
||||||
|
"00800001" /* max packet */
|
||||||
|
"21" /* character set (UTF-8) */
|
||||||
|
"000000000000000000000000" /* 23 bytes, al zeroes */
|
||||||
|
"0000000000000000000000"
|
||||||
|
"%[var(check.username),hex]00" /* the username */
|
||||||
|
"00" /* filler (always 0x00) */
|
||||||
|
"010000" /* packet length*/
|
||||||
|
"00" /* sequence ID */
|
||||||
|
"01" /* COM_QUIT command */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tcpcheck_ruleset *rs = NULL;
|
||||||
|
struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
|
||||||
|
struct tcpcheck_rule *chk;
|
||||||
|
struct tcpcheck_var *var = NULL;
|
||||||
|
char *mysql_rsname = "*mysql-check";
|
||||||
|
char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
|
||||||
|
int index = 0, err_code = 0;
|
||||||
|
|
||||||
|
if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
|
||||||
|
if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
|
||||||
|
ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
|
||||||
|
file, line);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
curpx->options2 &= ~PR_O2_CHK_ANY;
|
||||||
|
curpx->options2 |= PR_O2_TCPCHK_CHK;
|
||||||
|
|
||||||
|
free_tcpcheck_vars(&rules->preset_vars);
|
||||||
|
rules->list = NULL;
|
||||||
|
rules->flags = 0;
|
||||||
|
|
||||||
|
cur_arg += 2;
|
||||||
|
if (*args[cur_arg]) {
|
||||||
|
char *user;
|
||||||
|
int packetlen, userlen;
|
||||||
|
|
||||||
|
if (strcmp(args[cur_arg], "user") != 0) {
|
||||||
|
ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
|
||||||
|
file, line, args[0], args[1], args[cur_arg]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*(args[cur_arg+1]) == 0) {
|
||||||
|
ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
|
||||||
|
file, line, args[0], args[1], args[cur_arg]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = calloc(4, sizeof(*hdr));
|
||||||
|
user = strdup(args[cur_arg+1]);
|
||||||
|
userlen = strlen(args[cur_arg+1]);
|
||||||
|
|
||||||
|
if (hdr == NULL || user == NULL) {
|
||||||
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*args[cur_arg+2]) {
|
||||||
|
if (strcmp(args[cur_arg+2], "post-41") != 0) {
|
||||||
|
ha_alert("parsing [%s:%d] : keyword '%s' only supports option 'post-41' (got '%s').\n",
|
||||||
|
file, line, args[cur_arg], args[cur_arg+2]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
packetlen = userlen + 7 + 27;
|
||||||
|
mysql_req = mysql41_req;
|
||||||
|
mysql_rsname = mysql41_rsname;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
packetlen = userlen + 7;
|
||||||
|
mysql_req = mysql40_req;
|
||||||
|
mysql_rsname = mysql40_rsname;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr[0] = (unsigned char)(packetlen & 0xff);
|
||||||
|
hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
|
||||||
|
hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
|
||||||
|
hdr[3] = 1;
|
||||||
|
|
||||||
|
var = tcpcheck_var_create("check.header");
|
||||||
|
if (var == NULL) {
|
||||||
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
var->data.type = SMP_T_STR;
|
||||||
|
var->data.u.str.area = hdr;
|
||||||
|
var->data.u.str.data = 4;
|
||||||
|
LIST_INIT(&var->list);
|
||||||
|
LIST_ADDQ(&rules->preset_vars, &var->list);
|
||||||
|
hdr = NULL;
|
||||||
|
var = NULL;
|
||||||
|
|
||||||
|
var = tcpcheck_var_create("check.username");
|
||||||
|
if (var == NULL) {
|
||||||
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
var->data.type = SMP_T_STR;
|
||||||
|
var->data.u.str.area = user;
|
||||||
|
var->data.u.str.data = strlen(user);
|
||||||
|
LIST_INIT(&var->list);
|
||||||
|
LIST_ADDQ(&rules->preset_vars, &var->list);
|
||||||
|
user = NULL;
|
||||||
|
var = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs = tcpcheck_ruleset_lookup(mysql_rsname);
|
||||||
|
if (rs)
|
||||||
|
goto ruleset_found;
|
||||||
|
|
||||||
|
rs = tcpcheck_ruleset_create(mysql_rsname);
|
||||||
|
if (rs == NULL) {
|
||||||
|
ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
|
||||||
|
1, curpx, &rs->rules, file, line, &errmsg);
|
||||||
|
if (!chk) {
|
||||||
|
ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
chk->index = index++;
|
||||||
|
LIST_ADDQ(&rs->rules, &chk->list);
|
||||||
|
|
||||||
|
if (mysql_req) {
|
||||||
|
chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", mysql_req, "log-format", ""},
|
||||||
|
1, curpx, &rs->rules, file, line, &errmsg);
|
||||||
|
if (!chk) {
|
||||||
|
ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
chk->index = index++;
|
||||||
|
LIST_ADDQ(&rs->rules, &chk->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
|
||||||
|
1, curpx, &rs->rules, file, line, &errmsg);
|
||||||
|
if (!chk) {
|
||||||
|
ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
chk->expect.custom = tcpcheck_mysql_expect_iniths;
|
||||||
|
chk->index = index++;
|
||||||
|
LIST_ADDQ(&rs->rules, &chk->list);
|
||||||
|
|
||||||
|
if (mysql_req) {
|
||||||
|
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
|
||||||
|
1, curpx, &rs->rules, file, line, &errmsg);
|
||||||
|
if (!chk) {
|
||||||
|
ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
chk->expect.custom = tcpcheck_mysql_expect_ok;
|
||||||
|
chk->index = index++;
|
||||||
|
LIST_ADDQ(&rs->rules, &chk->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_ADDQ(&tcpchecks_list, &rs->list);
|
||||||
|
|
||||||
|
ruleset_found:
|
||||||
|
rules->list = &rs->rules;
|
||||||
|
rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_MYSQL_CHK);
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(errmsg);
|
||||||
|
return err_code;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(hdr);
|
||||||
|
free(user);
|
||||||
|
free(var);
|
||||||
|
free_tcpcheck_vars(&rules->preset_vars);
|
||||||
|
tcpcheck_ruleset_release(rs);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct cfg_kw_list cfg_kws = {ILH, {
|
static struct cfg_kw_list cfg_kws = {ILH, {
|
||||||
{ CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
|
{ CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck },
|
||||||
{ 0, NULL, NULL },
|
{ 0, NULL, NULL },
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user