Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

global: Allow display names in email addresses #2377

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions doc/man/man5/keepalived.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,16 @@ possibly following any cleanup actions needed.
\fBshutdown_script\fR SCRIPT_NAME [username [groupname]]
\fBshutdown_script_timeout\fR SECONDS # range [1,1000]

# Set of email To: notify
# Set of email To: notify. To include a display name, the whole email address
# must be included in double quotes(").
\fBnotification_email \fR{
[email protected]
[email protected] "My admin <[email protected]>"
...
}

# email from address that will be in the header
# (default: keepalived@<local host name>)
# email from address that will be in the header (see comment above for
# including a display name).
# (default: keepalived@local_host_name)
\fBnotification_email_from \[email protected]

# Remote SMTP server used to send notification email.
Expand Down
67 changes: 65 additions & 2 deletions keepalived/core/global_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ free_email_list(list_head_t *l)
email_t *email, *email_tmp;

list_for_each_entry_safe(email, email_tmp, l, e_list) {
FREE(email->addr);
FREE_CONST_PTR(email->addr);
FREE(email);
}
}
Expand All @@ -149,14 +149,77 @@ dump_email_list(FILE *fp, const list_head_t *l)
conf_write(fp, " %s", email->addr);
}

const char *
format_email_addr(const char *addr)
{
char *new_addr;
size_t len = strlen(addr);
const char *end_description;
const char *quote_char;
unsigned num_esc;
const char *ip;
char *op;

if (addr[len - 1] != '>')
return STRDUP(addr);

if (!(end_description = strrchr(addr, '<'))) {
/* We don't have a starting < - at the moment log it and copy verbatim */
log_message(LOG_INFO, "email address '%s' invalid", addr);
return STRDUP(addr);
}

/* Skip over white-space before < */
end_description--;
while (end_description > addr &&
(*end_description == ' ' ||
*end_description == '\t'))
end_description--;

/* We can't have a '"' because alloc_strvec_r() doesn't support it.
* We might be able to use alloc_strvec_quoted_escaped(), in which
* case we probably can have embedded '"'s. */

/* Do we have any of the characters that need quoting - see RFC5322 3.2.3? */
quote_char = strpbrk(addr, "()<>[]:;@\\,.");
if (!quote_char || quote_char > end_description)
return STRDUP(addr);

/* We need to quote any embedded '"'s or '\'s */
quote_char = addr;
num_esc = 0;
while ((quote_char = strpbrk(quote_char, "\"\\")) &&
quote_char <= end_description) {
num_esc++;
quote_char++;
}

new_addr = MALLOC(len + 2 + num_esc + 1);

ip = addr;
op = new_addr;
*op++ = '"';
while ((quote_char = strpbrk(ip, "\"\\")) &&
quote_char <= end_description) {
strncpy(op, ip, quote_char - ip);
op += quote_char - ip;
*op++ = '\\';
*op++ = *quote_char++;
ip = quote_char;
}
sprintf(op, "%.*s\"%s", (int)(end_description - ip + 1), ip, end_description + 1);

return new_addr;
}

void
alloc_email(const char *addr)
{
email_t *email;

PMALLOC(email);
INIT_LIST_HEAD(&email->e_list);
email->addr = STRDUP(addr);
email->addr = format_email_addr(addr);

list_add_tail(&email->e_list, &global_data->email);
}
Expand Down
12 changes: 5 additions & 7 deletions keepalived/core/global_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,11 @@ emailfrom_handler(const vector_t *strvec)
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "emailfrom missing - ignoring");
return;
}
} else if (vector_size(strvec) > 2)
report_config_error(CONFIG_GENERAL_ERROR, "emailfrom - ignoring extra entries '%s' ...", strvec_slot(strvec, 2));

FREE_CONST_PTR(global_data->email_from);
global_data->email_from = set_value(strvec);
global_data->email_from = format_email_addr(strvec_slot(strvec, 1));
}
static void
smtpto_handler(const vector_t *strvec)
Expand Down Expand Up @@ -253,17 +254,14 @@ email_handler(const vector_t *strvec)
{
const vector_t *email_vec = read_value_block(strvec);
unsigned int i;
char *str;

if (!email_vec) {
report_config_error(CONFIG_GENERAL_ERROR, "Warning - empty notification_email block");
return;
}

for (i = 0; i < vector_size(email_vec); i++) {
str = vector_slot(email_vec, i);
alloc_email(str);
}
for (i = 0; i < vector_size(email_vec); i++)
alloc_email(vector_slot(email_vec, i));

free_strvec(email_vec);
}
Expand Down
108 changes: 51 additions & 57 deletions keepalived/core/smtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
#include "scheduler.h"
#endif

/* If it suspected that one of subject, body, buffer or email_to
/* If it suspected that one of subject, body or buffer
* is overflowing in the smtp_t structure, defining SMTP_MSG_ALLOC_DEBUG
* when using --enable-mem-check should help identity the issue
*/
Expand Down Expand Up @@ -103,7 +103,6 @@ free_smtp_msg_data(smtp_t * smtp)
FREE(smtp->buffer);
FREE(smtp->subject);
FREE(smtp->body);
FREE(smtp->email_to);
#endif
FREE(smtp);
}
Expand All @@ -119,13 +118,11 @@ alloc_smtp_msg_data(void)
smtp->subject = (char *)MALLOC(MAX_HEADERS_LENGTH);
smtp->body = (char *)MALLOC(MAX_BODY_LENGTH);
smtp->buffer = (char *)MALLOC(SMTP_BUFFER_MAX);
smtp->email_to = (char *)MALLOC(SMTP_BUFFER_MAX);
#else
smtp = MALLOC(sizeof(smtp_t) + MAX_HEADERS_LENGTH + MAX_BODY_LENGTH + SMTP_BUFFER_MAX + SMTP_BUFFER_MAX);
smtp->subject = (char *)smtp + sizeof(smtp_t);
smtp->body = smtp->subject + MAX_HEADERS_LENGTH;
smtp->buffer = smtp->body + MAX_BODY_LENGTH;
smtp->email_to = smtp->buffer + SMTP_BUFFER_MAX;
#endif

return smtp;
Expand Down Expand Up @@ -378,14 +375,21 @@ smtp_read_thread(thread_ref_t thread)
static void
helo_cmd(__attribute__((unused)) thread_ref_t thread)
{
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), SMTP_HELO_CMD, (global_data->smtp_helo_name) ? global_data->smtp_helo_name : "localhost");
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "HELO %s\r\n", (global_data->smtp_helo_name) ? global_data->smtp_helo_name : "localhost");
}

/* MAIL command processing */
static void
mail_cmd(__attribute__((unused)) thread_ref_t thread)
{
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), SMTP_MAIL_CMD, global_data->email_from);
size_t len;
const char *start;

len = strlen(global_data->email_from);
if (global_data->email_from[len - 1] == '>' && (start = strrchr(global_data->email_from, '<')))
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "MAIL FROM:%s\r\n", start);
else
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "MAIL FROM:<%s>\r\n", global_data->email_from);
}

/* RCPT command processing */
Expand All @@ -394,6 +398,8 @@ rcpt_cmd(thread_ref_t thread)
{
smtp_t *smtp = THREAD_ARG(thread);
email_t *email = smtp->next_email_element;
size_t len;
const char *start;

/* We send RCPT TO command multiple time to add all our email receivers.
* --rfc821.3.1
Expand All @@ -403,7 +409,11 @@ rcpt_cmd(thread_ref_t thread)
else
smtp->next_email_element = list_entry(email->e_list.next, email_t, e_list);

snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), SMTP_RCPT_CMD, email->addr);
len = strlen(email->addr);
if (email->addr[len - 1] == '>' && (start = strrchr(email->addr, '<')))
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "RCPT TO:%s\r\n", start);
else
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "RCPT TO:<%s>\r\n", email->addr);
}
static void
rcpt_code(thread_ref_t thread)
Expand All @@ -418,7 +428,7 @@ rcpt_code(thread_ref_t thread)
static void
data_cmd(__attribute__((unused)) thread_ref_t thread)
{
strncpy(smtp_send_buffer, SMTP_DATA_CMD, sizeof(smtp_send_buffer));
strncpy(smtp_send_buffer, "DATA\r\n", sizeof(smtp_send_buffer));
}

/* BODY command processing.
Expand All @@ -432,14 +442,36 @@ body_cmd(thread_ref_t thread)
char rfc822[80]; /* Mon, 01 Mar 2021 09:44:08 +0000 */
time_t now;
struct tm t;
size_t offs = 0;
email_t *email;

time(&now);
localtime_r(&now, &t);
strftime(rfc822, sizeof(rfc822), "%a, %d %b %Y %H:%M:%S %z", &t);

/* send the DATA fields */
snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), SMTP_HEADERS_CMD SMTP_BODY_CMD "%s",
rfc822, global_data->email_from, smtp->subject, smtp->email_to, smtp->body, SMTP_SEND_CMD);
offs = snprintf(smtp_send_buffer, sizeof(smtp_send_buffer),
"Date: %s\r\n"
"From: %s\r\n"
"Subject: %s\r\n"
"X-Mailer: Keepalived\r\n"
"To:",
rfc822, global_data->email_from, smtp->subject);

/* Add the recipients */
list_for_each_entry(email, &global_data->email, e_list) {
offs += snprintf(smtp_send_buffer + offs, sizeof(smtp_send_buffer) - offs,
"%s %s",
list_is_first(&email->e_list, &global_data->email) ? "" : ",\r\n",
email->addr);
}

/* Now the message body */
snprintf(smtp_send_buffer + offs, sizeof(smtp_send_buffer) - offs,
"\r\n\r\n"
"%s\r\n"
"\r\n.\r\n",
smtp->body);
}
static void
body_code(thread_ref_t thread)
Expand All @@ -456,7 +488,7 @@ body_code(thread_ref_t thread)
static void
quit_cmd(__attribute__((unused)) thread_ref_t thread)
{
strncpy(smtp_send_buffer, SMTP_QUIT_CMD, sizeof(smtp_send_buffer));
strncpy(smtp_send_buffer, "QUIT\r\n", sizeof(smtp_send_buffer));
}

static void
Expand Down Expand Up @@ -515,6 +547,7 @@ smtp_log_to_file(smtp_t *smtp)
char time_buf[25];
int time_buf_len;
const char *file_name;
email_t *email;

file_name = make_tmp_filename("smtp-alert.log");
fp = fopen_safe(file_name, "a");
Expand All @@ -525,10 +558,15 @@ smtp_log_to_file(smtp_t *smtp)
localtime_r(&now, &tm);
time_buf_len = strftime(time_buf, sizeof time_buf, "%a %b %e %X %Y", &tm);

fprintf(fp, "%s: %s -> %s\n"
fprintf(fp, "%s: %s ->", time_buf, global_data->email_from);
list_for_each_entry(email, &global_data->email, e_list)
fprintf(fp, "%s %s",
list_is_first(&email->e_list, &global_data->email) ? "" : ",",
email->addr);

fprintf(fp, "\n"
"%*sSubject: %s\n"
"%*sBody: %s\n\n",
time_buf, global_data->email_from, smtp->email_to,
time_buf_len - 7, "", smtp->subject,
time_buf_len - 7, "", smtp->body);

Expand All @@ -539,48 +577,6 @@ smtp_log_to_file(smtp_t *smtp)
}
#endif

/*
* Build a comma separated string of smtp recipient email addresses
* for the email message To-header.
*/
static void
build_to_header_rcpt_addrs(smtp_t *smtp)
{
char *email_to_addrs;
size_t bytes_available = SMTP_BUFFER_MAX - 1;
size_t bytes_to_write;
bool done_addr = false;
email_t *email;

if (smtp == NULL)
return;

email_to_addrs = smtp->email_to;

list_for_each_entry(email, &global_data->email, e_list) {
bytes_to_write = strlen(email->addr);
if (done_addr) {
if (bytes_available < 2)
break;

/* Prepend with a comma and space to all non-first email addresses */
*email_to_addrs++ = ',';
*email_to_addrs++ = ' ';
bytes_available -= 2;
}
else
done_addr = true;

if (bytes_available < bytes_to_write)
break;

strcpy(email_to_addrs, email->addr);

email_to_addrs += bytes_to_write;
bytes_available -= bytes_to_write;
}
}

/* Main entry point */
void
smtp_alert(smtp_msg_t msg_type, void* data, const char *subject, const char *body)
Expand Down Expand Up @@ -656,8 +652,6 @@ smtp_alert(smtp_msg_t msg_type, void* data, const char *subject, const char *bod
strncpy(smtp->body, body, MAX_BODY_LENGTH - 1);
smtp->body[MAX_BODY_LENGTH - 1]= '\0';

build_to_header_rcpt_addrs(smtp);

#ifdef _SMTP_ALERT_DEBUG_
if (do_smtp_alert_debug)
smtp_log_to_file(smtp);
Expand Down
7 changes: 6 additions & 1 deletion keepalived/core/snmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ snmp_mail(struct variable *vp, oid *name, size_t *length,
{
email_t *email;
list_head_t *e;
struct { /* We need to cast aware const */
u_char *uc;
const u_char *cuc;
} ret;

if ((e = snmp_header_list_head_table(vp, name, length, exact,
var_len, write_method,
Expand All @@ -339,7 +343,8 @@ snmp_mail(struct variable *vp, oid *name, size_t *length,
switch (vp->magic) {
case SNMP_MAIL_EMAILADDRESS:
*var_len = strlen(email->addr);
return PTR_CAST(u_char, email->addr);
ret.cuc = PTR_CAST_CONST(u_char, email->addr);
return ret.uc;
default:
break;
}
Expand Down
3 changes: 2 additions & 1 deletion keepalived/include/global_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ extern void _start(void), etext(void);

/* email link list */
typedef struct _email {
char *addr;
const char *addr;

/* Linked list member */
list_head_t e_list;
Expand Down Expand Up @@ -298,6 +298,7 @@ extern data_t *global_data; /* Global configuration data */
extern data_t *old_global_data; /* Old global configuration data - used during reload */

/* Prototypes */
extern const char * format_email_addr(const char *);
extern void alloc_email(const char *);
extern data_t *alloc_global_data(void);
extern void init_global_data(data_t *, data_t *, bool);
Expand Down
Loading
Loading