mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-10 17:17:06 +02:00
The health-check attached to an email alert has no type. It is unexpected, and since the 2.6, it is important because we rely on it to know the application type in front of a connection at the stream-connector level. Because the object type is not set, the SE descriptor is not properly initialized, leading to a segfault when a connection to the SMTP server is established. This patch must be backported to 2.6 and may be backported as far as 2.0. However, it is only an issue for the 2.6 and upper.
322 lines
8.0 KiB
C
322 lines
8.0 KiB
C
/*
|
|
* Mailer management.
|
|
*
|
|
* Copyright 2015 Horms Solutions Ltd, Simon Horman <horms@verge.net.au>
|
|
* Copyright 2020 Willy Tarreau <w@1wt.eu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <haproxy/action-t.h>
|
|
#include <haproxy/api.h>
|
|
#include <haproxy/check.h>
|
|
#include <haproxy/errors.h>
|
|
#include <haproxy/list.h>
|
|
#include <haproxy/mailers.h>
|
|
#include <haproxy/pool.h>
|
|
#include <haproxy/proxy-t.h>
|
|
#include <haproxy/server-t.h>
|
|
#include <haproxy/task.h>
|
|
#include <haproxy/tcpcheck.h>
|
|
#include <haproxy/thread.h>
|
|
#include <haproxy/time.h>
|
|
#include <haproxy/tools.h>
|
|
|
|
|
|
struct mailers *mailers = NULL;
|
|
|
|
DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
|
|
|
|
/****************************** Email alerts ******************************/
|
|
/* NOTE: It may be pertinent to use an applet to handle email alerts */
|
|
/* instead of a tcp-check ruleset */
|
|
/**************************************************************************/
|
|
void email_alert_free(struct email_alert *alert)
|
|
{
|
|
struct tcpcheck_rule *rule, *back;
|
|
|
|
if (!alert)
|
|
return;
|
|
|
|
if (alert->rules.list) {
|
|
list_for_each_entry_safe(rule, back, alert->rules.list, list) {
|
|
LIST_DELETE(&rule->list);
|
|
free_tcpcheck(rule, 1);
|
|
}
|
|
free_tcpcheck_vars(&alert->rules.preset_vars);
|
|
ha_free(&alert->rules.list);
|
|
}
|
|
pool_free(pool_head_email_alert, alert);
|
|
}
|
|
|
|
static struct task *process_email_alert(struct task *t, void *context, unsigned int state)
|
|
{
|
|
struct check *check = context;
|
|
struct email_alertq *q;
|
|
struct email_alert *alert;
|
|
|
|
q = container_of(check, typeof(*q), check);
|
|
|
|
HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
while (1) {
|
|
if (!(check->state & CHK_ST_ENABLED)) {
|
|
if (LIST_ISEMPTY(&q->email_alerts)) {
|
|
/* All alerts processed, queue the task */
|
|
t->expire = TICK_ETERNITY;
|
|
task_queue(t);
|
|
goto end;
|
|
}
|
|
|
|
alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
|
|
LIST_DELETE(&alert->list);
|
|
t->expire = now_ms;
|
|
check->tcpcheck_rules = &alert->rules;
|
|
check->status = HCHK_STATUS_INI;
|
|
check->state |= CHK_ST_ENABLED;
|
|
}
|
|
|
|
process_chk(t, context, state);
|
|
if (check->state & CHK_ST_INPROGRESS)
|
|
break;
|
|
|
|
alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
|
|
email_alert_free(alert);
|
|
check->tcpcheck_rules = NULL;
|
|
check->server = NULL;
|
|
check->state &= ~CHK_ST_ENABLED;
|
|
}
|
|
end:
|
|
HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
return t;
|
|
}
|
|
|
|
/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
|
|
*
|
|
* The function returns 1 in success case, otherwise, it returns 0 and err is
|
|
* filled.
|
|
*/
|
|
int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
|
|
{
|
|
struct mailer *mailer;
|
|
struct email_alertq *queues;
|
|
const char *err_str;
|
|
int i = 0;
|
|
|
|
if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
|
|
memprintf(err, "out of memory while allocating mailer alerts queues");
|
|
goto fail_no_queue;
|
|
}
|
|
|
|
for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
|
|
struct email_alertq *q = &queues[i];
|
|
struct check *check = &q->check;
|
|
struct task *t;
|
|
|
|
LIST_INIT(&q->email_alerts);
|
|
HA_SPIN_INIT(&q->lock);
|
|
check->obj_type = OBJ_TYPE_CHECK;
|
|
check->inter = mls->timeout.mail;
|
|
check->rise = DEF_AGENT_RISETIME;
|
|
check->proxy = p;
|
|
check->fall = DEF_AGENT_FALLTIME;
|
|
if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
|
|
memprintf(err, "%s", err_str);
|
|
goto error;
|
|
}
|
|
|
|
check->xprt = mailer->xprt;
|
|
check->addr = mailer->addr;
|
|
check->port = get_host_port(&mailer->addr);
|
|
|
|
if ((t = task_new_anywhere()) == NULL) {
|
|
memprintf(err, "out of memory while allocating mailer alerts task");
|
|
goto error;
|
|
}
|
|
|
|
check->task = t;
|
|
t->process = process_email_alert;
|
|
t->context = check;
|
|
|
|
/* check this in one ms */
|
|
t->expire = TICK_ETERNITY;
|
|
check->start = now;
|
|
task_queue(t);
|
|
}
|
|
|
|
mls->users++;
|
|
free(p->email_alert.mailers.name);
|
|
p->email_alert.mailers.m = mls;
|
|
p->email_alert.queues = queues;
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; i < mls->count; i++) {
|
|
struct email_alertq *q = &queues[i];
|
|
struct check *check = &q->check;
|
|
|
|
free_check(check);
|
|
}
|
|
free(queues);
|
|
fail_no_queue:
|
|
return 1;
|
|
}
|
|
|
|
static int enqueue_one_email_alert(struct proxy *p, struct server *s,
|
|
struct email_alertq *q, const char *msg)
|
|
{
|
|
struct email_alert *alert;
|
|
struct tcpcheck_rule *tcpcheck;
|
|
struct check *check = &q->check;
|
|
|
|
if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
|
|
goto error;
|
|
LIST_INIT(&alert->list);
|
|
alert->rules.flags = TCPCHK_RULES_TCP_CHK;
|
|
alert->rules.list = calloc(1, sizeof(*alert->rules.list));
|
|
if (!alert->rules.list)
|
|
goto error;
|
|
LIST_INIT(alert->rules.list);
|
|
LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
|
|
alert->srv = s;
|
|
|
|
if ((tcpcheck = pool_zalloc(pool_head_tcpcheck_rule)) == NULL)
|
|
goto error;
|
|
tcpcheck->action = TCPCHK_ACT_CONNECT;
|
|
tcpcheck->comment = NULL;
|
|
|
|
LIST_APPEND(alert->rules.list, &tcpcheck->list);
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[4] = { "HELO ", p->email_alert.myhostname, "\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[2] = { "DATA\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
|
|
goto error;
|
|
|
|
{
|
|
struct tm tm;
|
|
char datestr[48];
|
|
const char * const strs[18] = {
|
|
"From: ", p->email_alert.from, "\r\n",
|
|
"To: ", p->email_alert.to, "\r\n",
|
|
"Date: ", datestr, "\r\n",
|
|
"Subject: [HAProxy Alert] ", msg, "\r\n",
|
|
"\r\n",
|
|
msg, "\r\n",
|
|
"\r\n",
|
|
".\r\n",
|
|
NULL
|
|
};
|
|
|
|
get_localtime(date.tv_sec, &tm);
|
|
|
|
if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[2] = { "QUIT\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
|
|
goto error;
|
|
|
|
HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
task_wakeup(check->task, TASK_WOKEN_MSG);
|
|
LIST_APPEND(&q->email_alerts, &alert->list);
|
|
HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
return 1;
|
|
|
|
error:
|
|
email_alert_free(alert);
|
|
return 0;
|
|
}
|
|
|
|
static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
|
|
{
|
|
int i;
|
|
struct mailer *mailer;
|
|
|
|
for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
|
|
i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
|
|
if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
|
|
ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Send email alert if configured.
|
|
*/
|
|
void send_email_alert(struct server *s, int level, const char *format, ...)
|
|
{
|
|
va_list argp;
|
|
char buf[1024];
|
|
int len;
|
|
struct proxy *p = s->proxy;
|
|
|
|
if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
|
|
return;
|
|
|
|
va_start(argp, format);
|
|
len = vsnprintf(buf, sizeof(buf), format, argp);
|
|
va_end(argp);
|
|
|
|
if (len < 0 || len >= sizeof(buf)) {
|
|
ha_alert("Email alert [%s] could not format message\n", p->id);
|
|
return;
|
|
}
|
|
|
|
enqueue_email_alert(p, s, buf);
|
|
}
|