#include "dma.h"
-#define DP ": \t\n"
-#define EQS " \t\n"
+#define DP ": \t"
+#define EQS " \t"
/*
}
}
-/*
- * Add a virtual user entry to the list of virtual users
- */
static void
-add_virtuser(char *login, char *address)
+chomp(char *str)
{
- struct virtuser *v;
+ size_t len = strlen(str);
- v = malloc(sizeof(struct virtuser));
- v->login = strdup(login);
- v->address = strdup(address);
- SLIST_INSERT_HEAD(&virtusers, v, next);
+ if (len == 0)
+ return;
+ if (str[len - 1] == '\n')
+ str[len - 1] = 0;
}
/*
* Read the virtual user table
*/
-int
+void
parse_virtuser(const char *path)
{
+ char line[2048];
FILE *v;
- char *word;
char *data;
- char line[2048];
+ struct virtuser *vu;
+ int lineno = 0;
v = fopen(path, "r");
- if (v == NULL)
- return (-1);
+ if (v == NULL) {
+ errlog(1, "can not open virtuser file `%s'", path);
+ /* NOTREACHED */
+ }
while (!feof(v)) {
if (fgets(line, sizeof(line), v) == NULL)
break;
- /* We hit a comment */
- if (strchr(line, '#'))
- *strchr(line, '#') = 0;
- if ((word = strtok(line, DP)) != NULL) {
- data = strtok(NULL, DP);
- if (data != NULL) {
- add_virtuser(word, data);
- }
- }
- }
+ lineno++;
- fclose(v);
- return (0);
-}
+ chomp(line);
-/*
- * Add entry to the SMTP auth user list
- */
-static int
-add_smtp_auth_user(char *userstring, char *password)
-{
- struct authuser *a;
- char *temp;
+ /* We hit a comment */
+ if (*line == '#')
+ continue;
+ /* Ignore empty lines */
+ if (*line == 0)
+ continue;
- a = malloc(sizeof(struct virtuser));
- a->password= strdup(password);
+ vu = calloc(1, sizeof(*vu));
+ if (vu == NULL)
+ errlog(1, NULL);
- temp = strrchr(userstring, '|');
- if (temp == NULL)
- return (-1);
+ data = strdup(line);
+ vu->login = strsep(&data, DP);
+ vu->address = data;
- a->host = strdup(temp+1);
- a->login = strdup(strtok(userstring, "|"));
- if (a->login == NULL)
- return (-1);
+ if (vu->login == NULL ||
+ vu->address == NULL) {
+ errlogx(1, "syntax error in virtuser file %s:%d",
+ path, lineno);
+ /* NOTREACHED */
+ }
- SLIST_INSERT_HEAD(&authusers, a, next);
+ SLIST_INSERT_HEAD(&virtusers, vu, next);
+ }
- return (0);
+ fclose(v);
}
/*
* Read the SMTP authentication config file
+ *
+ * file format is:
+ * user|host:password
+ *
+ * A line starting with # is treated as comment and ignored.
*/
-int
+void
parse_authfile(const char *path)
{
+ char line[2048];
+ struct authuser *au;
FILE *a;
- char *word;
char *data;
- char line[2048];
+ int lineno = 0;
a = fopen(path, "r");
- if (a == NULL)
- return (-1);
+ if (a == NULL) {
+ errlog(1, "can not open auth file `%s'", path);
+ /* NOTREACHED */
+ }
while (!feof(a)) {
if (fgets(line, sizeof(line), a) == NULL)
break;
+ lineno++;
+
+ chomp(line);
+
/* We hit a comment */
- if (strchr(line, '#'))
- *strchr(line, '#') = 0;
- if ((word = strtok(line, DP)) != NULL) {
- data = strtok(NULL, DP);
- if (data != NULL) {
- if (add_smtp_auth_user(word, data) < 0)
- return (-1);
- }
+ if (*line == '#')
+ continue;
+ /* Ignore empty lines */
+ if (*line == 0)
+ continue;
+
+ au = calloc(1, sizeof(*au));
+ if (au == NULL)
+ errlog(1, NULL);
+
+ data = strdup(line);
+ au->login = strsep(&data, "|");
+ au->host = strsep(&data, DP);
+ au->password = data;
+
+ if (au->login == NULL ||
+ au->host == NULL ||
+ au->password == NULL) {
+ errlogx(1, "syntax error in authfile %s:%d",
+ path, lineno);
+ /* NOTREACHED */
}
+
+ SLIST_INSERT_HEAD(&authusers, au, next);
}
fclose(a);
- return (0);
}
/*
* XXX TODO
- * Check if the user supplied a value. If not, fill in default
* Check for bad things[TM]
*/
-int
+void
parse_conf(const char *config_path)
{
char *word;
char *data;
FILE *conf;
char line[2048];
+ int lineno = 0;
conf = fopen(config_path, "r");
- if (conf == NULL)
- return (-1);
-
- /* Reset features */
- config->features = 0;
+ if (conf == NULL) {
+ /* Don't treat a non-existing config file as error */
+ if (errno == ENOENT)
+ return;
+ errlog(1, "can not open config `%s'", config_path);
+ /* NOTREACHED */
+ }
while (!feof(conf)) {
if (fgets(line, sizeof(line), conf) == NULL)
break;
+ lineno++;
+
+ chomp(line);
+
/* We hit a comment */
if (strchr(line, '#'))
*strchr(line, '#') = 0;
- if ((word = strtok(line, EQS)) != NULL) {
- data = strtok(NULL, EQS);
- if (strcmp(word, "SMARTHOST") == 0) {
- if (data != NULL)
- config->smarthost = strdup(data);
- }
- else if (strcmp(word, "PORT") == 0) {
- if (data != NULL)
- config->port = atoi(strdup(data));
- }
- else if (strcmp(word, "ALIASES") == 0) {
- if (data != NULL)
- config->aliases = strdup(data);
- }
- else if (strcmp(word, "SPOOLDIR") == 0) {
- if (data != NULL)
- config->spooldir = strdup(data);
- }
- else if (strcmp(word, "VIRTPATH") == 0) {
- if (data != NULL)
- config->virtualpath = strdup(data);
- }
- else if (strcmp(word, "AUTHPATH") == 0) {
- if (data != NULL)
- config->authpath= strdup(data);
- }
- else if (strcmp(word, "CERTFILE") == 0) {
- if (data != NULL)
- config->certfile = strdup(data);
- }
- else if (strcmp(word, "MAILNAME") == 0) {
- if (data != NULL)
- config->mailname = strdup(data);
- }
- else if (strcmp(word, "MAILNAMEFILE") == 0) {
- if (data != NULL)
- config->mailnamefile = strdup(data);
- }
- else if (strcmp(word, "VIRTUAL") == 0)
- config->features |= VIRTUAL;
- else if (strcmp(word, "STARTTLS") == 0)
- config->features |= STARTTLS;
- else if (strcmp(word, "SECURETRANSFER") == 0)
- config->features |= SECURETRANS;
- else if (strcmp(word, "DEFER") == 0)
- config->features |= DEFER;
- else if (strcmp(word, "INSECURE") == 0)
- config->features |= INSECURE;
- else if (strcmp(word, "FULLBOUNCE") == 0)
- config->features |= FULLBOUNCE;
- else {
- errno = EINVAL;
- return (-1);
- }
+
+ data = line;
+ word = strsep(&data, EQS);
+
+ /* Ignore empty lines */
+ if (word == NULL || *word == 0)
+ continue;
+
+ if (data != NULL && *data != 0)
+ data = strdup(data);
+ else
+ data = NULL;
+
+ if (strcmp(word, "SMARTHOST") == 0 && data != NULL)
+ config.smarthost = data;
+ else if (strcmp(word, "PORT") == 0 && data != NULL)
+ config.port = atoi(data);
+ else if (strcmp(word, "ALIASES") == 0 && data != NULL)
+ config.aliases = data;
+ else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL)
+ config.spooldir = data;
+ else if (strcmp(word, "VIRTPATH") == 0 && data != NULL)
+ config.virtualpath = data;
+ else if (strcmp(word, "AUTHPATH") == 0 && data != NULL)
+ config.authpath= data;
+ else if (strcmp(word, "CERTFILE") == 0 && data != NULL)
+ config.certfile = data;
+ else if (strcmp(word, "MAILNAME") == 0 && data != NULL)
+ config.mailname = data;
+ else if (strcmp(word, "MAILNAMEFILE") == 0 && data != NULL)
+ config.mailnamefile = data;
+ else if (strcmp(word, "VIRTUAL") == 0 && data == NULL)
+ config.features |= VIRTUAL;
+ else if (strcmp(word, "STARTTLS") == 0 && data == NULL)
+ config.features |= STARTTLS;
+ else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
+ config.features |= SECURETRANS;
+ else if (strcmp(word, "DEFER") == 0 && data == NULL)
+ config.features |= DEFER;
+ else if (strcmp(word, "INSECURE") == 0 && data == NULL)
+ config.features |= INSECURE;
+ else if (strcmp(word, "FULLBOUNCE") == 0 && data == NULL)
+ config.features |= FULLBOUNCE;
+ else {
+ errlogx(1, "syntax error in %s:%d", config_path, lineno);
+ /* NOTREACHED */
}
}
fclose(conf);
- return (0);
}
-
}
/* User supplied a certificate */
- if (config->certfile != NULL) {
- error = init_cert_file(ctx, config->certfile);
+ if (config.certfile != NULL) {
+ error = init_cert_file(ctx, config.certfile);
if (error) {
syslog(LOG_WARNING, "remote delivery deferred");
return (1);
if (((feature & SECURETRANS) != 0) &&
(feature & STARTTLS) != 0) {
/* TLS init phase, disable SSL_write */
- config->features |= NOSSL;
+ config.features |= NOSSL;
send_remote_command(fd, "EHLO %s", hostname());
if (read_remote(fd, 0, NULL) == 2) {
if (read_remote(fd, 0, NULL) != 2) {
syslog(LOG_ERR, "remote delivery deferred:"
" STARTTLS not available: %s", neterr);
- config->features &= ~NOSSL;
+ config.features &= ~NOSSL;
return (1);
}
}
/* End of TLS init phase, enable SSL_write/read */
- config->features &= ~NOSSL;
+ config.features &= ~NOSSL;
}
- config->ssl = SSL_new(ctx);
- if (config->ssl == NULL) {
+ config.ssl = SSL_new(ctx);
+ if (config.ssl == NULL) {
syslog(LOG_NOTICE, "remote delivery deferred: SSL struct creation failed: %s",
ssl_errstr());
return (1);
}
/* Set ssl to work in client mode */
- SSL_set_connect_state(config->ssl);
+ SSL_set_connect_state(config.ssl);
/* Set fd for SSL in/output */
- error = SSL_set_fd(config->ssl, fd);
+ error = SSL_set_fd(config.ssl, fd);
if (error == 0) {
syslog(LOG_NOTICE, "remote delivery deferred: SSL set fd failed: %s",
ssl_errstr());
}
/* Open SSL connection */
- error = SSL_connect(config->ssl);
+ error = SSL_connect(config.ssl);
if (error < 0) {
syslog(LOG_ERR, "remote delivery deferred: SSL handshake failed fatally: %s",
ssl_errstr());
}
/* Get peer certificate */
- cert = SSL_get_peer_certificate(config->ssl);
+ cert = SSL_get_peer_certificate(config.ssl);
if (cert == NULL) {
syslog(LOG_WARNING, "remote delivery deferred: Peer did not provide certificate: %s",
ssl_errstr());
#include "dma.h"
-
static void deliver(struct qitem *);
struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
struct virtusers virtusers = LIST_HEAD_INITIALIZER(virtusers);
struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
-struct config *config;
const char *username;
const char *logident_base;
static int daemonize = 1;
+struct config config = {
+ .smarthost = NULL,
+ .port = 25,
+ .aliases = "/var/mail/aliases",
+ .spooldir = "/var/spool/dma",
+ .virtualpath = NULL,
+ .authpath = NULL,
+ .certfile = NULL,
+ .features = 0,
+ .mailname = NULL,
+ .mailnamefile = NULL,
+};
+
+
static char *
set_from(struct queue *queue, const char *osender)
{
struct virtuser *v;
char *sender;
- if ((config->features & VIRTUAL) != 0) {
+ if ((config.features & VIRTUAL) != 0) {
SLIST_FOREACH(v, &virtusers, next) {
if (strcmp(v->login, username) == 0) {
sender = strdup(v->address);
static int
read_aliases(void)
{
- yyin = fopen(config->aliases, "r");
- if (yyin == NULL)
- return (0); /* not fatal */
+ yyin = fopen(config.aliases, "r");
+ if (yyin == NULL) {
+ /*
+ * Non-existing aliases file is not a fatal error
+ */
+ if (errno == ENOENT)
+ return (0);
+ /* Other problems are. */
+ return (-1);
+ }
if (yyparse())
return (-1); /* fatal error, probably malloc() */
fclose(yyin);
/* XXX fork root here */
- config = calloc(1, sizeof(*config));
- if (config == NULL)
- errlog(1, NULL);
+ parse_conf(CONF_PATH);
- if (parse_conf(CONF_PATH) < 0) {
- free(config);
- errlog(1, "can not read config file");
+ if (config.features & VIRTUAL) {
+ if (config.virtualpath == NULL)
+ errlogx(1, "no virtuser file specified, but VIRTUAL configured");
+ parse_virtuser(config.virtualpath);
}
- if (config->features & VIRTUAL)
- if (parse_virtuser(config->virtualpath) < 0)
- errlog(1, "can not read virtual user file `%s'",
- config->virtualpath);
-
- if (parse_authfile(config->authpath) < 0)
- errlog(1, "can not read SMTP authentication file");
+ if (config.authpath != NULL)
+ parse_authfile(config.authpath);
if (showq) {
if (load_queue(&queue) < 0)
}
if (read_aliases() != 0)
- errlog(1, "can not read aliases file `%s'", config->aliases);
+ errlog(1, "can not read aliases file `%s'", config.aliases);
if ((sender = set_from(&queue, sender)) == NULL)
errlog(1, NULL);
/* From here on the mail is safe. */
- if (config->features & DEFER || queue_only)
+ if (config.features & DEFER || queue_only)
return (0);
run_queue(&queue);
};
struct config {
- char *smarthost;
+ const char *smarthost;
int port;
- char *aliases;
- char *spooldir;
- char *virtualpath;
- char *authpath;
- char *certfile;
+ const char *aliases;
+ const char *spooldir;
+ const char *virtualpath;
+ const char *authpath;
+ const char *certfile;
int features;
+ const char *mailname;
+ const char *mailnamefile;
+
+ /* XXX does not belong into config */
SSL *ssl;
- char *mailname;
- char *mailnamefile;
};
/* global variables */
extern struct aliases aliases;
-extern struct config *config;
+extern struct config config;
extern struct strlist tmpfs;
extern struct virtusers virtusers;
extern struct authusers authusers;
/* conf.c */
void trim_line(char *);
-int parse_conf(const char *);
-int parse_virtuser(const char *);
-int parse_authfile(const char *);
+void parse_conf(const char *);
+void parse_virtuser(const char *);
+void parse_authfile(const char *);
/* crypto.c */
void hmac_md5(unsigned char *, int, unsigned char *, int, caddr_t);
VERSION, hostname(),
it->addr,
reason,
- config->features & FULLBOUNCE ?
+ config.features & FULLBOUNCE ?
"Original message follows." :
"Message headers follow.");
if (error < 0)
if (fseek(it->mailf, 0, SEEK_SET) != 0)
goto fail;
- if (config->features & FULLBOUNCE) {
+ if (config.features & FULLBOUNCE) {
while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
if (fwrite(line, 1, pos, bounceq.mailf) != pos)
goto fail;
strcat(cmd, "\r\n");
len = strlen(cmd);
- if (((config->features & SECURETRANS) != 0) &&
- ((config->features & NOSSL) == 0)) {
- while ((s = SSL_write(config->ssl, (const char*)cmd, len)) <= 0) {
- s = SSL_get_error(config->ssl, s);
+ if (((config.features & SECURETRANS) != 0) &&
+ ((config.features & NOSSL) == 0)) {
+ while ((s = SSL_write(config.ssl, (const char*)cmd, len)) <= 0) {
+ s = SSL_get_error(config.ssl, s);
if (s != SSL_ERROR_WANT_READ &&
s != SSL_ERROR_WANT_WRITE) {
strncpy(neterr, ssl_errstr(), sizeof(neterr));
memmove(buff, buff + pos, len - pos);
len -= pos;
pos = 0;
- if (((config->features & SECURETRANS) != 0) &&
- (config->features & NOSSL) == 0) {
- if ((rlen = SSL_read(config->ssl, buff + len,
+ if (((config.features & SECURETRANS) != 0) &&
+ (config.features & NOSSL) == 0) {
+ if ((rlen = SSL_read(config.ssl, buff + len,
sizeof(buff) - len)) == -1) {
strncpy(neterr, ssl_errstr(), sizeof(neterr));
return (-1);
return (1);
}
- if ((config->features & INSECURE) != 0 ||
- (config->features & SECURETRANS) != 0) {
+ if ((config.features & INSECURE) != 0 ||
+ (config.features & SECURETRANS) != 0) {
/* Send AUTH command according to RFC 2554 */
send_remote_command(fd, "AUTH LOGIN");
if (read_remote(fd, 0, NULL) != 3) {
static void
close_connection(int fd)
{
- if (((config->features & SECURETRANS) != 0) &&
- ((config->features & NOSSL) == 0))
- SSL_shutdown(config->ssl);
+ if (((config.features & SECURETRANS) != 0) &&
+ ((config.features & NOSSL) == 0))
+ SSL_shutdown(config.ssl);
- if (config->ssl != NULL)
- SSL_free(config->ssl);
+ if (config.ssl != NULL)
+ SSL_free(config.ssl);
close(fd);
}
return (1);
/* Check first reply from remote host */
- config->features |= NOSSL;
+ config.features |= NOSSL;
res = read_remote(fd, 0, NULL);
if (res != 2) {
syslog(LOG_WARNING, "Invalid initial response: %i", res);
return(1);
}
- config->features &= ~NOSSL;
+ config.features &= ~NOSSL;
- if ((config->features & SECURETRANS) != 0) {
- error = smtp_init_crypto(fd, config->features);
+ if ((config.features & SECURETRANS) != 0) {
+ error = smtp_init_crypto(fd, config.features);
if (error >= 0)
syslog(LOG_DEBUG, "SSL initialization successful");
else
* Check if the user wants plain text login without using
* encryption.
*/
- syslog(LOG_INFO, "using SMTP authentication");
+ syslog(LOG_INFO, "using SMTP authentication for user %s", a->login);
error = smtp_login(fd, a->login, a->password);
if (error < 0) {
syslog(LOG_ERR, "remote delivery failed:"
/* asprintf can't take const */
void *errmsgc = __DECONST(char **, errmsg);
struct mx_hostentry *hosts, *h;
- char *host;
+ const char *host;
int port;
int error = 1, smarthost = 0;
port = SMTP_PORT;
/* Smarthost support? */
- if (config->smarthost != NULL && strlen(config->smarthost) > 0) {
- syslog(LOG_INFO, "using smarthost (%s:%i)",
- config->smarthost, config->port);
- host = config->smarthost;
+ if (config.smarthost != NULL) {
+ host = config.smarthost;
+ port = config.port;
+ syslog(LOG_INFO, "using smarthost (%s:%i)", host, port);
smarthost = 1;
-
- if (config->port != 0)
- port = config->port;
}
error = dns_get_mx_list(host, port, &hosts, smarthost);
struct stritem *t;
int fd;
- if (snprintf(fn, sizeof(fn), "%s/%s", config->spooldir, "tmp_XXXXXXXXXX") <= 0)
+ if (snprintf(fn, sizeof(fn), "%s/%s", config.spooldir, "tmp_XXXXXXXXXX") <= 0)
return (-1);
fd = mkstemp(fn);
LIST_FOREACH(it, &queue->queue, next) {
if (asprintf(&it->queueid, "%s.%"PRIxPTR, queue->id, (uintptr_t)it) <= 0)
goto delfiles;
- if (asprintf(&it->queuefn, "%s/Q%s", config->spooldir, it->queueid) <= 0)
+ if (asprintf(&it->queuefn, "%s/Q%s", config.spooldir, it->queueid) <= 0)
goto delfiles;
- if (asprintf(&it->mailfn, "%s/M%s", config->spooldir, it->queueid) <= 0)
+ if (asprintf(&it->mailfn, "%s/M%s", config.spooldir, it->queueid) <= 0)
goto delfiles;
/* Neither file may not exist yet */
bzero(queue, sizeof(queue));
LIST_INIT(&queue->queue);
- spooldir = opendir(config->spooldir);
+ spooldir = opendir(config.spooldir);
if (spooldir == NULL)
err(1, "reading queue");
continue;
if (de->d_name[0] != 'Q')
continue;
- if (asprintf(&queuefn, "%s/Q%s", config->spooldir, de->d_name + 1) < 0)
+ if (asprintf(&queuefn, "%s/Q%s", config.spooldir, de->d_name + 1) < 0)
goto fail;
- if (asprintf(&mailfn, "%s/M%s", config->spooldir, de->d_name + 1) < 0)
+ if (asprintf(&mailfn, "%s/M%s", config.spooldir, de->d_name + 1) < 0)
goto fail;
if (stat(mailfn, &sb) != 0)
if (initialized)
return (name);
- if (config->mailname != NULL && config->mailname[0] != '\0') {
- snprintf(name, sizeof(name), "%s", config->mailname);
+ if (config.mailname != NULL && config.mailname[0] != '\0') {
+ snprintf(name, sizeof(name), "%s", config.mailname);
initialized = 1;
return (name);
}
- if (config->mailnamefile != NULL && config->mailnamefile[0] != '\0') {
- fp = fopen(config->mailnamefile, "r");
+ if (config.mailnamefile != NULL && config.mailnamefile[0] != '\0') {
+ fp = fopen(config.mailnamefile, "r");
if (fp != NULL) {
if (fgets(name, sizeof(name), fp) != NULL) {
len = strlen(name);