/*
* Copyright (c) 2007 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Mail delivery monitoring daemon. This program executes round-trip tests
* to ensure that the mail system is performing.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "pathnames.h"
#include "smtp.h"
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
char *smtpHost = "localhost";
char *smtpPort = "2525";
char *testSndr = "root@localhost";
char *testRcpt = "mailmond@localhost";
char *testMaildir = "/var/mailmond/Maildir/";
char *testSpamdir = "/var/mailmond/Maildir/.Spam/";
int testIval = 30;
int testDelayMax = 60;
int Verbose = 0;
char *SpamSample, *HamSample;
size_t SpamSampleLen = 0;
size_t HamSampleLen = 0;
volatile int die_signal = 0;
volatile int die_flag = 0;
static int
LoadConfig(const char *path)
{
char *data, *line, *pLine;
size_t dataLen;
FILE *f;
if ((f = fopen(path, "r")) == NULL) {
fprintf(stderr, "%s: %s\n", path, strerror(errno));
return (-1);
}
fseek(f, 0, SEEK_END); dataLen = (size_t)ftell(f);
fseek(f, 0, SEEK_SET);
if ((data = malloc(dataLen)) == NULL) {
fprintf(stderr, "Out of memory\n");
return (-1);
}
if (fread(data, dataLen, 1, f) < 1) {
fprintf(stderr, "Error reading config file\n");
fclose(f);
return (-1);
}
pLine = data;
while ((line = strsep(&pLine, "\n")) != NULL) {
char *s = line;
char *key, *val;
if (s[0] == '#' || s[0] == '\0') {
continue;
}
key = strsep(&line, "=");
val = strsep(&line, "=");
if (key == NULL || val == NULL) {
continue;
}
if (!strcasecmp(key,"smtp.host")) { smtpHost = val; }
else if (!strcasecmp(key,"smtp.port")) { smtpPort = val; }
else if (!strcasecmp(key,"test.sender")) { testSndr = val; }
else if (!strcasecmp(key,"test.recipient")) { testRcpt = val; }
else if (!strcasecmp(key,"test.maildir")) { testMaildir = val; }
else if (!strcasecmp(key,"test.spamdir")) { testSpamdir = val; }
else if (!strcasecmp(key,"test.interval")) {
testIval = atoi(val);
} else if (!strcasecmp(key,"test.delayMax")) {
testDelayMax = atoi(val);
} else {
fprintf(stderr, "%s: No such option: %s\n", path, key);
}
}
fclose(f);
printf("SMTP host = %s:%s\n", smtpHost, smtpPort);
printf("Test sender = %s\n", testSndr);
printf("Test recipient = %s\n", testRcpt);
printf("Test maildir = %s\n", testMaildir);
printf("Test spamdir = %s\n", testSpamdir);
printf("Test interval = %d\n", testIval);
printf("Test maximum delay = %d\n", testDelayMax);
return (0);
}
static int
LoadSamples(void)
{
FILE *f;
if ((f = fopen("spam1.txt", "r")) == NULL) {
fprintf(stderr, "spam1.txt: %s\n", strerror(errno));
return (-1);
}
fseek(f, 0, SEEK_END); SpamSampleLen = (size_t)ftell(f);
fseek(f, 0, SEEK_SET);
if ((SpamSample = malloc(SpamSampleLen)) == NULL) {
fprintf(stderr, "Out of memory\n");
fclose(f);
return (-1);
}
if (fread(SpamSample, SpamSampleLen, 1, f) < 1) {
fprintf(stderr, "Error reading spam sample\n");
fclose(f);
return (-1);
}
fclose(f);
if ((f = fopen("nonspam1.txt", "r")) == NULL) {
fprintf(stderr, "nonspam1.txt: %s\n", strerror(errno));
return (-1);
}
fseek(f, 0, SEEK_END); HamSampleLen = (size_t)ftell(f);
fseek(f, 0, SEEK_SET);
if ((HamSample = malloc(HamSampleLen)) == NULL) {
fprintf(stderr, "Out of memory\n");
fclose(f);
return (-1);
}
if (fread(HamSample, HamSampleLen, 1, f) < 1) {
fprintf(stderr, "Error reading spam sample\n");
fclose(f);
return (-1);
}
fclose(f);
return (0);
}
static void
Alarm(void)
{
syslog(LOG_ERR, "Mail system failure");
printf("FAILURE\n");
#if 0
printf("Stopping Postfix\n");
system("/usr/bin/sudo /usr/local/sbin/postfix.stop");
sleep(5);
printf("Stopping mailprocd\n");
system("/usr/bin/sudo /usr/local/sbin/mailprocd.stop");
sleep(30);
printf("Starting mailprocd\n");
system("/usr/bin/sudo /usr/local/sbin/mailprocd.run");
sleep(15);
printf("Starting postfix\n");
system("/usr/bin/sudo /usr/local/sbin/postfix.run");
#endif
}
static int
CheckMaildir(const char *maildir)
{
char path[MAXPATHLEN];
struct dirent *dent;
DIR *dir;
int nMsgs = 0;
strlcpy(path, maildir, sizeof(path));
strlcat(path, "new", sizeof(path));
if ((dir = opendir(path)) == NULL) {
fprintf(stderr, "%s: %s\n", path, strerror(errno));
return (-1);
}
while ((dent = readdir(dir)) != NULL) {
if (dent->d_name[0] == '.') {
continue;
}
strlcpy(path, maildir, sizeof(path));
strlcat(path, "new/", sizeof(path));
strlcat(path, dent->d_name, sizeof(path));
if (unlink(path) == -1) {
fprintf(stderr, "rm %s: %s\n", path, strerror(errno));
}
nMsgs++;
}
closedir(dir);
return (nMsgs > 0) ? 0 : -1;
}
static void
sig_die(int sigraised)
{
die_signal = sigraised;
die_flag = 1;
}
int
main(int argc, char *argv[])
{
struct sigaction sa;
extern char *__progname;
const char *cfgPath = _PATH_MAILMOND_CONF;
int c;
while ((c = getopt(argc, argv, "Vc:?")) != -1) {
switch (c) {
case 'V':
Verbose = 1;
break;
case 'c':
cfgPath = optarg;
break;
default:
printf("Usage: %s [-V] [-c cfg-file]\n", __progname);
break;
}
}
if (LoadConfig(cfgPath) == -1 ||
LoadSamples() == -1)
exit(1);
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = sig_die;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
for (;;) {
if (Verbose) {
printf("[%s:%s] Test\n", smtpHost, smtpPort);
}
if (SMTP_Inject(SpamSample, SpamSampleLen, testSndr, testRcpt,
smtpHost, smtpPort) == 0 &&
SMTP_Inject(HamSample, HamSampleLen, testSndr, testRcpt,
smtpHost, smtpPort) == 0) {
if (die_flag) { goto cleanup; }
sleep(testDelayMax);
if (die_flag) { goto cleanup; }
if (CheckMaildir(testMaildir) == -1 ||
CheckMaildir(testSpamdir) == -1) {
Alarm();
}
if (Verbose) {
printf("[%s:%s] OK\n", smtpHost, smtpPort);
}
} else {
Alarm();
}
if (die_flag) { goto cleanup; }
sleep(testIval);
if (die_flag) { goto cleanup; }
}
cleanup:
syslog(LOG_NOTICE, "exiting on signal %d", die_signal);
fprintf(stderr, "Exiting on signal %d\n", die_signal);
CheckMaildir(testMaildir);
CheckMaildir(testSpamdir);
return (0);
}