amavis-milter-based-on-1.1.2.3.2.40.c [plain text]
#include "config.h"
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <syslog.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <limits.h>
#include <grp.h>
#include <pwd.h>
#ifdef HAVE_SM_GEN_H
# include "sm/gen.h"
#endif
#include "libmilter/mfapi.h"
#ifndef HAVE_SM_GEN_BOOL_TYPE
typedef int bool;
#endif
#define BUFFLEN 255
#define SOCKBUFLEN 8192
#ifndef RUNTIME_DIR
# define RUNTIME_DIR "/var/amavis"
#endif
#ifndef AMAVISD_SOCKET
# define AMAVISD_SOCKET RUNTIME_DIR ## "/amavisd.sock"
#endif
#define D_TEMPLATE "/amavis-milter-XXXXXXXX"
#define F_TEMPLATE "/email.txt"
#define DEVNULL "/dev/null"
#ifndef X_HEADER_TAG
# define X_HEADER_TAG "X-Virus-Scanned"
#endif
#ifndef X_HEADER_LINE
# define X_HEADER_LINE "by amavisd-milter (http://www.amavis.org/)"
#endif
#define DBG_NONE 0
#define DBG_FATAL 1
#define DBG_WARN 2
#define DBG_INFO 3
#define DBG_DEBUG 4
typedef struct llstrct {
char *str;
struct llstrct *next;
} ll;
struct mlfiPriv {
struct in_addr client_addr;
char *mlfi_fname;
FILE *mlfi_fp;
char *mlfi_envfrom;
ll mlfi_envto;
ll *mlfi_thisenvto;
int mlfi_numto;
};
static int verbosity;
static int AM_DAEMON = 1;
static struct group *miltergroup;
static gid_t amavis_gid;
static struct utsname amavis_uts;
static int enable_x_header = 1;
static void amavis_syslog(const int, const char *, ...);
static char *amavis_mkdtemp(char *);
static int group_member(const char *);
static void freeenvto(ll *);
static sfsistat clearpriv(SMFICTX *, sfsistat, int);
static int allocmem(SMFICTX *);
static sfsistat mlfi_connect(SMFICTX *, char *, _SOCK_ADDR *);
static sfsistat mlfi_envto(SMFICTX *, char **);
static sfsistat mlfi_envfrom(SMFICTX *, char **);
static sfsistat mlfi_header(SMFICTX *, char *, char *);
static sfsistat mlfi_eoh(SMFICTX *);
static sfsistat mlfi_body(SMFICTX *, u_char *, size_t);
static sfsistat mlfi_eom(SMFICTX *);
static sfsistat mlfi_close(SMFICTX *);
static sfsistat mlfi_abort(SMFICTX *);
static sfsistat mlfi_cleanup(SMFICTX *, bool);
static void
amavis_syslog(const int level, const char *fmt, ...)
{
time_t tmpt;
char *timestamp;
char buf[512];
va_list ap;
if (level > verbosity)
return;
buf[0] = 0;
va_start(ap, fmt);
if (AM_DAEMON == 0) {
tmpt = time(NULL);
timestamp = ctime(&tmpt);
timestamp[24] = 0;
snprintf(buf,sizeof(buf)-1,"%s %s amavis-milter[%ld]: ",
timestamp,
(amavis_uts.nodename ? amavis_uts.nodename : "localhost"),
(long) getpid());
}
vsnprintf(buf+strlen(buf),sizeof(buf)-strlen(buf)-1,fmt,ap);
va_end(ap);
if (AM_DAEMON == 0) {
fprintf(stderr,"%s\n",buf);
}
openlog("amavis-milter", LOG_PID|LOG_CONS, LOG_MAIL);
syslog(LOG_NOTICE,"%s\n",buf);
closelog();
}
static char *
amavis_mkdtemp(char *s)
{
char *stt;
int count = 0;
#ifdef HAVE_MKDTEMP
return mkdtemp(s);
#else
while (count++ < 20) {
# ifdef HAVE_MKTEMP
stt = mktemp(s);
# else
stt = strrchr(s, '-') + 1;
if (stt) {
snprintf(stt, strlen(s) - 1 - (stt - s), "%08d", lrand48() / 215);
stt = s;
} else {
return NULL;
}
# endif
if (stt) {
if (!mkdir(s, S_IRWXU)) {
return s;
} else {
continue;
}
}
}
return NULL;
#endif
}
static
int group_member(const char *group)
{
int i, r, rc = -1;
gid_t *grouplist = 0;
if (!(miltergroup = getgrnam(group))) {
perror("getgrnam");
return rc;
}
if ((r = getgroups(0, grouplist)) < 0) {
perror("getgroups");
} else if ((grouplist = malloc (r*sizeof(gid_t))) == NULL) {
perror("malloc");
r = 0;
} else if ((r = getgroups(r, grouplist)) < 0) {
perror("getgroups");
free(grouplist);
}
for (i=0;i<r;i++) {
if (miltergroup->gr_gid == grouplist[i]) {
rc = 0;
break;
}
}
if (grouplist)
free(grouplist);
return rc;
}
#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
static void
freeenvto(ll * envto)
{
ll *new;
while (envto) {
new = envto->next;
if (envto->str) {
free(envto->str);
envto->str = NULL;
}
if (envto)
free(envto);
envto = new;
}
}
static sfsistat
clearpriv(SMFICTX *ctx, sfsistat retme, int clearall)
{
struct mlfiPriv *priv = MLFIPRIV;
amavis_syslog(DBG_INFO, "Clearing priv (clearall=%d)", clearall);
if (priv) {
if (priv->mlfi_fname) {
amavis_syslog(DBG_INFO, "clearing fname");
free(priv->mlfi_fname);
priv->mlfi_fname = NULL;
}
if (priv->mlfi_envfrom) {
amavis_syslog(DBG_INFO, "clearing envfrom");
free(priv->mlfi_envfrom);
priv->mlfi_envfrom = NULL;
}
if (priv->mlfi_envto.next) {
amavis_syslog(DBG_INFO, "clearing multi-envto");
freeenvto(priv->mlfi_envto.next);
priv->mlfi_envto.next = NULL;
}
if (priv->mlfi_envto.str) {
amavis_syslog(DBG_INFO, "clearing envto");
free(priv->mlfi_envto.str);
priv->mlfi_envto.str = NULL;
}
amavis_syslog(DBG_INFO, "clearing priv");
free(priv);
priv = NULL;
if (smfi_setpriv(ctx, priv) != MI_SUCCESS) {
amavis_syslog(DBG_WARN, "(clearpriv)smfi_setpriv failed");
}
}
return retme;
}
static int
allocmem(SMFICTX * ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
if (priv == NULL) {
amavis_syslog(DBG_INFO, "(allocmem)priv was null");
priv = malloc(sizeof *priv);
if (priv == NULL) {
amavis_syslog(DBG_FATAL, "failed to malloc %d bytes for private store: %s",
sizeof(*priv), strerror(errno));
return 1;
}
amavis_syslog(DBG_INFO, "malloced priv - now using memset()");
memset(priv, 0, sizeof *priv);
amavis_syslog(DBG_INFO, "malloced priv successfully");
if (smfi_setpriv(ctx, priv) != MI_SUCCESS) {
amavis_syslog(DBG_WARN, "(allocmem)smfi_setpriv failed");
}
} else {
amavis_syslog(DBG_WARN, "allocmem tried but priv was already set");
amavis_syslog(DBG_WARN, "priv->client_addr.s_addr is %d",
priv->client_addr.s_addr);
}
return 0;
}
static sfsistat
mlfi_connect(SMFICTX * ctx, char *hostname, _SOCK_ADDR * gen_hostaddr)
{
struct mlfiPriv *priv;
struct sockaddr_in *hostaddr;
hostaddr = (struct sockaddr_in *) gen_hostaddr;
if (hostaddr) {
amavis_syslog(DBG_INFO, "hostname is %s, addr is %d.%d.%d.%d",
hostname, (hostaddr->sin_addr.s_addr) & 0xff,
(hostaddr->sin_addr.s_addr >> 8) & 0xff,
(hostaddr->sin_addr.s_addr >> 16) & 0xff,
(hostaddr->sin_addr.s_addr >> 24) & 0xff);
}
amavis_syslog(DBG_INFO, "checking allocmem");
if (allocmem(ctx))
return SMFIS_TEMPFAIL;
priv = MLFIPRIV;
if (hostaddr) {
priv->client_addr.s_addr = hostaddr->sin_addr.s_addr;
} else {
priv->client_addr.s_addr = 0;
}
if (smfi_setpriv(ctx, priv) != MI_SUCCESS) {
amavis_syslog(DBG_WARN, "(mlfi_connect)smfi_setpriv failed");
}
return SMFIS_CONTINUE;
}
static sfsistat
mlfi_envto(SMFICTX * ctx, char **envto)
{
struct mlfiPriv *priv;
if (allocmem(ctx))
return SMFIS_TEMPFAIL;
priv = MLFIPRIV;
if (!(priv->mlfi_thisenvto)) {
priv->mlfi_thisenvto = &(priv->mlfi_envto);
priv->mlfi_numto = 1;
} else {
priv->mlfi_numto++;
if ((priv->mlfi_thisenvto->next = malloc(sizeof(ll))) == NULL)
return (SMFIS_TEMPFAIL);
priv->mlfi_thisenvto = priv->mlfi_thisenvto->next;
priv->mlfi_thisenvto->next = NULL;
}
if ((priv->mlfi_thisenvto->str = strdup(*envto)) == NULL)
return (SMFIS_TEMPFAIL);
amavis_syslog(DBG_INFO, "added %s as recip", *envto);
return SMFIS_CONTINUE;
}
static sfsistat
mlfi_envfrom(SMFICTX * ctx, char **envfrom)
{
struct mlfiPriv *priv;
struct stat StatBuf;
char *messagepath;
if (allocmem(ctx))
return SMFIS_TEMPFAIL;
priv = MLFIPRIV;
messagepath = malloc(strlen(RUNTIME_DIR) + strlen(D_TEMPLATE) + strlen(F_TEMPLATE) + 1);
if (messagepath == NULL) {
amavis_syslog(DBG_FATAL, "Failed to allocate memory for temp file name: %s", strerror(errno));
return SMFIS_TEMPFAIL;
}
strcpy(messagepath, RUNTIME_DIR);
strcat(messagepath, D_TEMPLATE);
umask(0007);
if (amavis_mkdtemp(messagepath) == NULL) {
amavis_syslog(DBG_FATAL, "Failed to create temp dir %s: %s", messagepath, strerror(errno));
return SMFIS_TEMPFAIL;
}
umask (0007);
if (lstat(messagepath, &StatBuf) < 0) {
amavis_syslog(DBG_FATAL, "Error while trying lstat(%s): %s", messagepath, strerror(errno));
return SMFIS_TEMPFAIL;
}
if (!S_ISDIR(StatBuf.st_mode) || StatBuf.st_uid != geteuid() || StatBuf.st_gid != getegid() || !(StatBuf.st_mode & S_IRWXU)) {
amavis_syslog(DBG_FATAL,
"Security Warning: %s must be a directory owned by "
"User %d and Group %d, and read-/write-able by the User "
"only. Exit.", messagepath, geteuid(), getegid());
return SMFIS_TEMPFAIL;
}
strcat(messagepath, F_TEMPLATE);
amavis_syslog(DBG_INFO, "received: %s; from=%s", messagepath, *envfrom);
priv->mlfi_fname = strdup(messagepath);
free(messagepath);
if (priv->mlfi_fname == NULL) {
return SMFIS_TEMPFAIL;
}
priv->mlfi_envfrom = strdup(*envfrom);
if (!priv->mlfi_envfrom) {
free(priv->mlfi_fname);
priv->mlfi_fname = NULL;
return SMFIS_TEMPFAIL;
}
if ((priv->mlfi_fp = fopen(priv->mlfi_fname, "w+")) == NULL) {
free(priv->mlfi_fname);
priv->mlfi_fname = NULL;
free(priv->mlfi_envfrom);
priv->mlfi_envfrom = NULL;
return SMFIS_TEMPFAIL;
}
if (smfi_setpriv(ctx, priv) != MI_SUCCESS) {
amavis_syslog(DBG_WARN, "(mlfi_envfrom)smfi_setpriv failed");
}
return SMFIS_CONTINUE;
}
static sfsistat
mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
{
fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv);
return SMFIS_CONTINUE;
}
static sfsistat
mlfi_eoh(SMFICTX *ctx)
{
fprintf(MLFIPRIV->mlfi_fp, "\n");
return SMFIS_CONTINUE;
}
static sfsistat
mlfi_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen)
{
u_char *d = bodyp, *s = bodyp;
u_char *lastc = bodyp + bodylen - 1;
while (s <= lastc) {
if (s != lastc && *s == 13 && *(s+1) == 10)
s++;
*d++ = *s++;
}
bodylen = (size_t)(d - bodyp);
if (bodylen && fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) {
(void) mlfi_cleanup(ctx, 0);
return SMFIS_TEMPFAIL;
}
return SMFIS_CONTINUE;
}
const char _EOT = '\3';
static sfsistat
mlfi_eom(SMFICTX *ctx)
{
struct mlfiPriv *priv = MLFIPRIV;
char buff[7];
int sock, r;
char *sender;
char retval;
struct sockaddr_un saddr;
if (!priv) {
amavis_syslog(DBG_WARN, "couldn't scan - no priv object");
return clearpriv(ctx, SMFIS_TEMPFAIL, 0);
}
amavis_syslog(DBG_INFO, "mlfi_eom()");
if (priv->mlfi_fp)
fclose(priv->mlfi_fp);
saddr.sun_family = AF_UNIX;
if (strlen(AMAVISD_SOCKET)+1 > sizeof(saddr.sun_path)) {
amavis_syslog(DBG_FATAL, "socket path too long: %d", strlen(AMAVISD_SOCKET));
exit(EX_TEMPFAIL);
}
strcpy(saddr.sun_path, AMAVISD_SOCKET);
amavis_syslog(DBG_INFO, "allocate socket()");
r = (sock = socket(PF_UNIX, SOCK_STREAM, 0));
if (r < 0) {
amavis_syslog(DBG_FATAL, "failed to allocate socket: %s", strerror(errno));
}
if (r >= 0) {
amavis_syslog(DBG_INFO, "mlfi_eom:connect");
r = connect(sock, (struct sockaddr *) (&saddr), sizeof(saddr));
if (r < 0)
amavis_syslog(DBG_FATAL, "failed to connect(): %s", strerror(errno));
}
if (r >= 0) {
char *p = strrchr(priv->mlfi_fname, '/');
amavis_syslog(DBG_INFO, "mlfi_eom:sendfile");
*p = 0;
r = send(sock, priv->mlfi_fname, strlen(priv->mlfi_fname), 0);
*p = '/';
if (r < 0)
amavis_syslog(DBG_FATAL, "failed to send() file name: %s", strerror(errno));
}
if (r >= 0) {
r = recv(sock, &retval, 1, 0);
if (r < 0)
amavis_syslog(DBG_FATAL, "failed to recv() file name confirmation: %s", strerror(errno));
}
if (r >= 0) {
size_t sender_l;
sender = (strlen(priv->mlfi_envfrom) > 0) ? priv->mlfi_envfrom : "<>";
amavis_syslog(DBG_INFO, "sendfrom() %s", sender);
sender_l = strlen(sender);
if (sender_l > SOCKBUFLEN) {
amavis_syslog(DBG_WARN, "Sender too long (%d), truncated to %d characters", sender_l, SOCKBUFLEN);
sender_l = SOCKBUFLEN;
}
r = send(sock, sender, sender_l, 0);
if (r < 0)
amavis_syslog(DBG_FATAL, "failed to send() Sender: %s", strerror(errno));
else if (r < sender_l)
amavis_syslog(DBG_WARN, "failed to send() complete Sender, truncated to %d characters ", r);
}
if (r >= 0) {
r = recv(sock, &retval, 1, 0);
if (r < 0)
amavis_syslog(DBG_FATAL, "failed to recv() ok for Sender info: %s", strerror(errno));
}
if (r >= 0) {
int x;
priv->mlfi_thisenvto = &(priv->mlfi_envto);
for (x = 0; (r >= 0) && (x < priv->mlfi_numto); x++) {
size_t recipient_l;
amavis_syslog(DBG_INFO, "sendto() %s", priv->mlfi_thisenvto->str);
recipient_l = strlen(priv->mlfi_thisenvto->str);
if (recipient_l > SOCKBUFLEN) {
amavis_syslog(DBG_WARN, "Recipient too long (%d), truncated to %d characters", recipient_l,SOCKBUFLEN);
recipient_l = SOCKBUFLEN;
}
r = send(sock, priv->mlfi_thisenvto->str, recipient_l, 0);
if (r < 0)
amavis_syslog(DBG_FATAL, "failed to send() Recipient: %s", strerror(errno));
else {
if (r < recipient_l)
amavis_syslog(DBG_WARN, "failed to send() complete Recipient, truncated to %d characters ", r);
r = recv(sock, &retval, 1, 0);
if (r < 0)
amavis_syslog(DBG_FATAL, "failed to recv() ok for recip info: %s", strerror(errno));
priv->mlfi_thisenvto = priv->mlfi_thisenvto->next;
}
}
}
if (r >= 0) {
amavis_syslog(DBG_INFO, "send() EOT");
r = send(sock, &_EOT, 1, 0);
if (r < 0) {
amavis_syslog(DBG_FATAL, "failed to send() EOT: %s", strerror(errno));
} else {
r = recv(sock, buff, 6, 0);
amavis_syslog(DBG_INFO, "received %s from daemon", buff);
if (r < 0)
amavis_syslog(DBG_FATAL, "Failed to recv() final result: %s", strerror(errno));
else if (!r)
amavis_syslog(DBG_FATAL, "Failed to recv() final result: empty status string");
}
}
close(sock);
if (r < 0) {
amavis_syslog(DBG_FATAL, "communication failure");
return clearpriv(ctx, SMFIS_TEMPFAIL, 0);
}
amavis_syslog(DBG_INFO, "finished conversation");
if (*buff)
retval = atoi(buff);
else
retval = 1;
amavis_syslog(DBG_INFO, "retval is %d", retval);
if (retval == 99) {
amavis_syslog(DBG_WARN, "discarding mail");
return clearpriv(ctx, SMFIS_DISCARD, 0);
}
if (retval == EX_UNAVAILABLE) {
amavis_syslog(DBG_WARN, "rejecting mail");
if (smfi_setreply(ctx, "550", "5.7.1", "Message content rejected") != MI_SUCCESS) {
amavis_syslog(DBG_WARN, "(mlfi_eom)smfi_setreply failed");
}
return clearpriv(ctx, SMFIS_REJECT, 0);
}
if (retval == 0) {
if (enable_x_header) {
if (smfi_chgheader(ctx, X_HEADER_TAG, 1, X_HEADER_LINE) == MI_FAILURE) {
amavis_syslog(DBG_INFO, "adding header");
if (smfi_addheader(ctx, X_HEADER_TAG, X_HEADER_LINE) != MI_SUCCESS) {
amavis_syslog(DBG_WARN, "(mlfi_eom)smfi_addheader failed");
}
} else
amavis_syslog(DBG_INFO, "header already present");
}
amavis_syslog(DBG_WARN, "returning ACCEPT");
return clearpriv(ctx, SMFIS_ACCEPT, 0);
}
amavis_syslog(DBG_WARN, "returning TEMPFAIL");
return clearpriv(ctx, SMFIS_TEMPFAIL, 0);
}
static sfsistat
mlfi_close(SMFICTX *ctx)
{
return clearpriv(ctx, SMFIS_ACCEPT, 1);
}
static sfsistat
mlfi_abort(SMFICTX *ctx)
{
return mlfi_cleanup(ctx, 0);
}
static sfsistat
mlfi_cleanup(SMFICTX *ctx, bool ok)
{
sfsistat rstat = SMFIS_CONTINUE;
struct mlfiPriv *priv = MLFIPRIV;
char *p;
if (!priv)
return rstat;
if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) {
rstat = SMFIS_TEMPFAIL;
(void) unlink(priv->mlfi_fname);
*(strrchr(priv->mlfi_fname, '/')) = 0;
rmdir(priv->mlfi_fname);
} else if (ok) {
p = strrchr(priv->mlfi_fname, '/');
if (p == NULL)
p = priv->mlfi_fname;
else
p++;
} else {
(void) unlink(priv->mlfi_fname);
*strrchr(priv->mlfi_fname, '/') = 0;
rmdir(priv->mlfi_fname);
}
amavis_syslog(DBG_INFO, "cleanup called");
return clearpriv(ctx, rstat, 0);
}
struct smfiDesc smfilter = {
"amavis-milter",
SMFI_VERSION,
SMFIF_ADDHDRS|SMFIF_CHGHDRS,
mlfi_connect,
NULL,
mlfi_envfrom,
mlfi_envto,
mlfi_header,
mlfi_eoh,
mlfi_body,
mlfi_eom,
mlfi_abort,
mlfi_close
};
void
usage(void)
{
fprintf(stderr, "usage:\n");
fprintf(stderr, " amavis-milter -p local:<unix-socket> [-d] [-v]\n");
fprintf(stderr, " amavis-milter -p inet:port@0.0.0.0 [-d] [-v]\n");
fprintf(stderr, " amavis-milter -h\n");
fprintf(stderr, "\n");
fprintf(stderr, "-p specifies a milter socket on which amavis-milter\n");
fprintf(stderr, " will listen for connections from sendmail.\n");
fprintf(stderr, " The argument is passed directly to libmilter, see sendmail milter\n");
fprintf(stderr, " documentation for details. The socket specified must match the\n");
fprintf(stderr, " INPUT_MAIL_FILTER macro call in the sendmail configuration file.\n");
fprintf(stderr, "-d debug: disables daemonisation and turns log level fully up (-vvvv) \n");
fprintf(stderr, "-v increases logging level by one, may be specified up to 4 times\n");
fprintf(stderr, "-h help: displays this usage text and exits\n");
fprintf(stderr, "\n");
fprintf(stderr, "Options -g, -x, -D are allowed for compatibility but ignored.\n");
fprintf(stderr, "\n");
fprintf(stderr, "This helper prgram (milter daemon) is normally started as:\n");
fprintf(stderr, "# su amavis -c '/usr/local/sbin/amavis-milter -p local:/var/amavis/amavis-milter.sock'\n");
};
int
main(int argc, char *argv[])
{
struct passwd *userinfo;
int c, i;
char *p, *milter_socket = NULL, *milter_socket_group = NULL;
const char *args = ":hdg:p:Dvx";
pid_t pid;
int devnull;
#if !defined(HAVE_MKDTEMP) && !defined(HAVE_MKTEMP)
int mypid = getpid();
srand48(time(NULL) ^ (mypid + (mypid << 15)));
#endif
umask(0007);
while ((c = getopt(argc, argv, args)) != -1) {
switch (c) {
case 'd':
verbosity = DBG_DEBUG;
AM_DAEMON = 0;
break;
case 'g':
if (optarg == NULL || *optarg == '\0') {
fprintf(stderr, "%s: Illegal group: %s\n", argv[0], optarg);
}
fprintf(stderr, "%s: group specification ignored (not implemented)\n", argv[0]);
milter_socket_group = strdup(optarg);
break;
case 'p':
if (optarg == NULL || *optarg == '\0') {
fprintf(stderr, "%s: Illegal conn: %s\n", argv[0], optarg);
exit(EXIT_FAILURE);
}
milter_socket = strdup(optarg);
break;
case 'v':
verbosity++;
break;
case 'D':
AM_DAEMON = 1;
break;
case 'x':
fprintf(stderr, "%s: option -x ignored to avoid confusion with older versions\n", argv[0]);
break;
case 'h':
usage();
exit(EXIT_SUCCESS);
break;
default:
usage();
exit(EXIT_FAILURE);
}
}
if (smfi_register(smfilter) == MI_FAILURE) {
fprintf(stderr, "%s: smfi_register failed\n", argv[0]);
exit(EXIT_FAILURE);
}
uname(&amavis_uts);
if (!milter_socket) {
fprintf(stderr, "%s: no milter socket specified (missing option -p)\n\n", argv[0]);
usage();
exit(EXIT_FAILURE);
}
if ((p = strchr(milter_socket,'/'))) {
if (unlink(p) < 0) {
amavis_syslog(DBG_INFO, "INFO: Cannot unlink old socket %s: %s", milter_socket, strerror(errno));
}
}
if (smfi_setconn(milter_socket) != MI_SUCCESS) {
amavis_syslog(DBG_FATAL, "(main)smfi_setconn failed");
}
if (AM_DAEMON == 1) {
if ((pid = fork()) > 0) {
amavis_syslog(DBG_INFO, "amavis-milter forked into background");
exit(EXIT_SUCCESS);
} else if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (setsid() < (pid_t)0) {
amavis_syslog(DBG_FATAL, "setsid() returned error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
if (chdir("/") < 0 ) {
amavis_syslog(DBG_FATAL, "chdir(/) returned error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
for (i = 0; i < _POSIX_OPEN_MAX ; i++) {
close(i);
}
if ((devnull = open(DEVNULL, O_RDONLY, 0)) < 0) {
amavis_syslog(DBG_FATAL, "Could not open %s as STDIN: %s", DEVNULL, strerror(errno));
exit(EXIT_FAILURE);
}
if (devnull != 0) {
amavis_syslog(DBG_FATAL, "Got wrong file descriptor as STDIN: %s != 0", DEVNULL);
exit(EXIT_FAILURE);
}
if ((devnull = open(DEVNULL, O_WRONLY, 0)) < 0) {
amavis_syslog(DBG_FATAL, "Could not open %s as STDOUT: %s", DEVNULL, strerror(errno));
exit(EXIT_FAILURE);
}
if (devnull != 1) {
amavis_syslog(DBG_FATAL, "Got wrong file descriptor as STDOUT: %s != 1", DEVNULL);
exit(EXIT_FAILURE);
}
if ((devnull = open(DEVNULL, O_WRONLY, 0)) < 0) {
amavis_syslog(DBG_FATAL, "Could not open %s as STDERR: %s", DEVNULL, strerror(errno));
exit(EXIT_FAILURE);
}
if (devnull != 2) {
amavis_syslog(DBG_FATAL, "Got wrong file descriptor as STDERR: %s != 2", DEVNULL);
exit(EXIT_FAILURE);
}
}
if (miltergroup && (setgid(miltergroup->gr_gid)) < 0) {
amavis_syslog(DBG_FATAL, "setgid(%d): %s", miltergroup->gr_gid, strerror(errno));
exit(EX_UNAVAILABLE);
}
amavis_syslog(DBG_INFO, "Starting, handing off to smfi_main");
return smfi_main();
}