/* * Mailer management. * * Copyright 2015 Horms Solutions Ltd, Simon Horman * Copyright 2020 Willy Tarreau * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct mailers *mailers = NULL; /* Set to 1 to disable email sending through checks even if the * mailers are configured to do so. (e.g.: disable from lua) */ int send_email_disabled = 0; 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

using 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_ns; 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 (send_email_disabled) return; 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); }