/* * Copyright (c) 2006-2008 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mailprocd.h" #include "pathnames.h" #include "ports.h" #include #include #include int mpdSoftFail; char *mpdHome, *mpdSafePath, *mpdSafeShell, *mpdSocketDir, *mpdPidFile; char *saUserDir, *saUserConfDir, *saUserPrefs; int saSocketBacklog, saMaxProcMsgs, saMaxIdle, saLearning, saMaxSize; int smtpEnable, smtpListenBacklog, smtpMaxClients; char *smtpHost, *smtpPort; int lmtpEnable, lmtpListenBacklog, lmtpMaxClients; char *lmtpSockPath; int polEnable, polListenBacklog; int ctlEnable, ctlListenBacklog; int mbdReportEnable; char *mbdHost, *mbdPort, *mbdPass; char *forceFilterDomains; float forceFilterThreshold; FILE *sfLog = NULL; #ifdef HAVE_PERL static PerlInterpreter *sfPerl; #endif static char sfError[1024]; static int smtpSocks[4], lmtpSock, polSock, ctlSock; static int smtpSockCount; static TAILQ_HEAD(,smtp_session) smtpSessions; static u_int smtpClientCount = 0, lmtpClientCount = 0; static TAILQ_HEAD(,qmgr_worker) qmgrWorkers; static u_int qmgrWorkersCount = 0; static volatile int die_flag = 0; static volatile int reload_flag = 0; static volatile int chld_flag = 0; static fd_set servfds; void SF_SetError(const char *fmt, ...) { char errBuf[1024]; va_list ap; va_start(ap, fmt); vsnprintf(errBuf, sizeof(errBuf), fmt, ap); va_end(ap); strlcpy(sfError, errBuf, sizeof(sfError)); } char * SF_GetError(void) { return (sfError); } void SF_Fatal(const char *fmt, ...) { char err[128]; va_list ap; va_start(ap, fmt); vsnprintf(err, sizeof(err), fmt, ap); va_end(ap); fputs(err, stderr); fputc('\n', stderr); exit(1); } void * SF_Malloc(size_t size) { void *p; if ((p = malloc(size)) == NULL) { Fatal("Out of memory"); } return (p); } #ifdef DEBUG void SF_Debug(const char *fmt, ...) { static char msg[160]; va_list ap; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); fprintf(sfLog, "[%d] %s\n", getpid(), msg); fflush(sfLog); } #endif /* DEBUG */ #ifdef HAVE_PERL static void xs_init (pTHX); EXTERN_C void boot_DynaLoader(pTHX_ CV *cv); EXTERN_C void xs_init(pTHX) { char *file = __FILE__; dXSUB_SYS; newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); } #endif /* HAVE_PERL */ static void ReportExit(const char *what, pid_t rpid, int code) { if (WIFSIGNALED(code)) { syslog(LOG_ERR, "[%d] %s exited (signal %d)", (int)rpid, what, WTERMSIG(code)); } else if (WIFEXITED(code) && WEXITSTATUS(code) != 0) { syslog(LOG_ERR, "[%d] %s exited due to failure (%d)", (int)rpid, what, WEXITSTATUS(code)); } } static void ReapCTLSession(CTL_Session *ctls, int exitCode) { Debug("ReapCTLSession: #%d (pid %d)", ctlClientCount-1, (int)ctls->pid); TAILQ_REMOVE(&ctlSessions, ctls, sessions); free(ctls); if (ctlClientCount == 0) { syslog(LOG_ERR, "Bogus control session count!"); } else { ctlClientCount--; } ReportExit("ctlsession", ctls->pid, exitCode); } static void ReapSMTPSession(SMTP_Session *smtps, int exitCode) { TAILQ_REMOVE(&smtpSessions, smtps, sessions); FD_CLR(smtps->pipe, &servfds); close(smtps->pipe); free(smtps); switch (smtps->prot) { case ESMTP_PROTOCOL: if (smtpClientCount == 0) { syslog(LOG_ERR, "Bogus SMTP client count!"); } else { smtpClientCount--; } ReportExit("smtpd", smtps->pid, exitCode); break; case LMTP_PROTOCOL: if (lmtpClientCount == 0) { syslog(LOG_ERR, "Bogus LMTP client count!"); } else { lmtpClientCount--; } ReportExit("lmtpd", smtps->pid, exitCode); break; } } static void ReapWorker(QMGR_Worker *worker, int exitCode) { #if 0 Debug("ReapWorker: #%d (pid %d)", qmgrWorkersCount-1, (int)worker->pid); #endif TAILQ_REMOVE(&qmgrWorkers, worker, workers); close(worker->pipe); free(worker); if (qmgrWorkersCount == 0) { syslog(LOG_ERR, "Bogus SMTP session count!"); } else { qmgrWorkersCount--; } ReportExit("worker", worker->pid, exitCode); } static void Reap(void) { int save_errno = errno; int rpid, code; do { rpid = waitpid(-1, &code, WNOHANG); if (polProc != 0 && rpid == polProc) { ReportExit("policy", rpid, code); polProc = 0; } else if (qmgrProc != 0 && rpid == qmgrProc) { ReportExit("qmgr", rpid, code); qmgrProc = 0; } else if (rpid > 0) { CTL_Session *sCtl; SMTP_Session *sSMTP; QMGR_Worker *worker; TAILQ_FOREACH(sSMTP, &smtpSessions, sessions) { if (rpid == sSMTP->pid) { ReapSMTPSession(sSMTP, code); break; } } if (sSMTP == NULL) { TAILQ_FOREACH(worker, &qmgrWorkers, workers) { if (rpid == worker->pid) { ReapWorker(worker, code); break; } } if (worker == NULL) { TAILQ_FOREACH(sCtl, &ctlSessions, sessions) { if (rpid == sCtl->pid) { ReapCTLSession(sCtl, code); break; } } if (sCtl == NULL) { ReportExit("mailprocd", rpid, code); } } } } } while (rpid > 0 || (rpid == -1 && errno == EINTR)); errno = save_errno; } static void child_sig_die(int sigraised) { _exit(0); } static void master_sig_die(int sigraised) { die_flag = 1; } static void master_sig_reload(int sigraised) { reload_flag = 1; } static void master_sig_chld(int sigraised) { chld_flag = 1; } static void CloseFiles(void) { SMTP_Session *smtps; int i; TAILQ_FOREACH(smtps, &smtpSessions, sessions) { close(smtps->pipe); } if (polEnable) { close(polSock); } if (ctlEnable) { close(ctlSock); } if (smtpEnable) { for (i = 0; i < smtpSockCount; i++) close(smtpSocks[i]); } if (lmtpEnable) { close(lmtpSock); } } void SF_EnterServerProc(const char *name) { struct sigaction sa; CloseFiles(); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_DFL; sigaction(SIGCHLD, &sa, NULL); sa.sa_handler = SIG_IGN; sigaction(SIGURG, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigfillset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = child_sig_die; sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); closelog(); openlog(name, LOG_PID|LOG_NDELAY, LOG_LOCAL0); Setproctitle("%s", name); } void SF_ExitServerProc(int code) { closelog(); exit(code); } static void SF_OpenLog(void) { int fd; if (sfLog != NULL) { fclose(sfLog); } if ((sfLog = fopen(_PATH_LOG_FILE, "a")) == NULL) { syslog(LOG_ERR, "%s: %m", _PATH_LOG_FILE); fprintf(stderr, "%s: %s\n", _PATH_LOG_FILE, strerror(errno)); exit(1); } fd = fileno(sfLog); fchmod(fd, 0640); fchown(fd, 0, 20); } static __inline__ int ProcessSignals(void) { if (die_flag) { die_flag = 0; if (kill(0, SIGUSR1) == -1) { syslog(LOG_ERR, "USR1: %m"); } return (1); } if (chld_flag) { chld_flag = 0; Reap(); } if (reload_flag) { reload_flag = 0; LOCAL_ReloadDatabases(); SF_OpenLog(); } return (0); } /* * Process a notification originating from a smtpd process. We simply * forward the notification to the worker process based on the UID, and * fork a new worker process as necessary. */ static int ProcessQueueNotification(SMTP_Session *smtps, const QMGR_MetaData *meta) { QMGR_Worker *worker; TAILQ_FOREACH(worker, &qmgrWorkers, workers) { if (worker->uid == meta->uid && worker->gid == meta->gid) break; } if (worker == NULL) { int pp[2]; pid_t pid; Debug("Creating new worker for %d:%d", meta->uid, meta->gid); if (pipe(pp) == -1) { syslog(LOG_ERR, "pipe(WORKER): %m"); return (-1); } if ((pid = fork()) == -1) { syslog(LOG_ERR, "fork(WORKER): %m"); close(pp[0]); close(pp[1]); return (-1); } else if (pid == 0) { /* Child */ dup2(pp[0], STDIN_FILENO); close(pp[1]); SF_EnterServerProc("mailprocd"); Setproctitle("-worker: %d", meta->uid); /* * NOTE: We could drop privileges here if we did not * allow users to assign e-mail addresses with multiple * recipients of possibly different UIDs. */ fcntl(STDIN_FILENO, F_SETOWN, getpid()); if (QMGR_WorkerMain(meta->uid) == -1) { Debug("WORKER failed: %s", SF_GetError()); syslog(LOG_ERR, "WORKER: %s", SF_GetError()); } SF_ExitServerProc(0); } else { worker = Malloc(sizeof(QMGR_Worker)); worker->pid = pid; worker->uid = meta->uid; worker->gid = meta->gid; worker->pipe = pp[1]; close(pp[0]); TAILQ_INSERT_TAIL(&qmgrWorkers, worker, workers); qmgrWorkersCount++; } } else { Debug("Found existing worker %d for %d:%d", worker->pid, worker->uid, worker->gid); if (worker->pid <= 1) { syslog(LOG_ERR, "Bad worker pid!"); } else { if (kill(worker->pid, SIGUSR2) == -1) { syslog(LOG_ERR, "USR2->worker%d: %s", (int)worker->pid, strerror(errno)); } } } return (0); } #ifdef HAVE_PERL static void InitPerl(int argc, char **argv, char **env) { char initPath[1024]; char *myArgv[2] = { "", initPath }; Strlcpy(initPath, SHAREDIR, sizeof(initPath)); Strlcat(initPath, "/init.pl", sizeof(initPath)); PERL_SYS_INIT3(&argc, &argv, &env); sfPerl = perl_alloc(); perl_construct(sfPerl); perl_parse(sfPerl, xs_init, 2, myArgv, (char **)NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_run(sfPerl); } #endif /* HAVE_PERL */ int main(int argc, char **argv, char **env) { struct addrinfo hints, *res, *res0; const char *cause = NULL; int rv, i, maxfd = 0; struct sigaction sa; my_socklen_t socklen; struct stat sb; FILE *f; struct sockaddr_un unaddr, paddr; struct passwd *pwd; pid_t pid; int pp[2]; SMTP_Session *smtps; CFG_File *cf; if ((cf = CFG_OpenFile(SYSCONFDIR, _PATH_CONFIG_FILE)) == NULL) { fprintf(stderr, "Load configuration file: %s\n", SF_GetError()); exit(1); } #ifdef HAVE_PERL InitPerl(argc, argv, env); #endif SF_OpenLog(); #ifdef HAVE_SA if (SA_GetVersion(saVersion, sizeof(saVersion)) == -1) { Fatal("Cannot get SA version"); } Debug("mailprocd %s (SpamAssassin %s)", VERSION, saVersion); syslog(LOG_INFO, "Server startup (%s with SpamAssassin %s)", VERSION, saVersion); SA_CompileNow(0, 0); #else Debug("mailprocd %s", VERSION); syslog(LOG_INFO, "Server startup (%s)", VERSION); #endif TAILQ_INIT(&smtpSessions); smtpClientCount = 0; lmtpClientCount = 0; TAILQ_INIT(&qmgrWorkers); qmgrWorkersCount = 0; CFG_GetInt(cf, "soft-fail", &mpdSoftFail, 0); CFG_GetStr(cf, "home", &mpdHome, _PATH_HOME); CFG_GetStr(cf, "socket-dir", &mpdSocketDir, _PATH_SOCKETDIR); CFG_GetStr(cf, "pid-file", &mpdPidFile, _PATH_PID); CFG_GetStr(cf, "safe-path", &mpdSafePath, _SAFE_PATH); CFG_GetStr(cf, "safe-shell", &mpdSafeShell, _SAFE_SHELL); CFG_GetStr(cf, "sa.user-dir", &saUserDir, _PATH_SAUSERDIR); CFG_GetStr(cf, "sa.user-conf-dir", &saUserConfDir, _PATH_SACONF); CFG_GetStr(cf, "sa.user-prefs", &saUserPrefs, _PATH_SAUSERPREFS); CFG_GetInt(cf, "sa.socket-backlog", &saSocketBacklog, 20); CFG_GetInt(cf, "sa.max-proc-msgs", &saMaxProcMsgs, 100); CFG_GetInt(cf, "sa.max-idle", &saMaxIdle, 60); CFG_GetInt(cf, "sa.learning", &saLearning, 1); CFG_GetInt(cf, "sa.max-size", &saMaxSize, 65536); CFG_GetInt(cf, "smtp.enable", &smtpEnable, 1); CFG_GetStr(cf, "smtp.host", &smtpHost, "localhost"); CFG_GetStr(cf, "smtp.port", &smtpPort, _PORT_MAILPROCD); CFG_GetInt(cf, "smtp.listen-backlog", &smtpListenBacklog, 10); CFG_GetInt(cf, "smtp.max-clients", &smtpMaxClients, 100); CFG_GetInt(cf, "lmtp.enable", &lmtpEnable, 1); CFG_GetInt(cf, "lmtp.listen-backlog", &lmtpListenBacklog, 10); CFG_GetStr(cf, "lmtp.socket-path", &lmtpSockPath, _PATH_LMTP_SOCKET); CFG_GetInt(cf, "lmtp.max-clients", &lmtpMaxClients, 100); CFG_GetInt(cf, "pol.enable", &polEnable, 0); CFG_GetInt(cf, "pol.listen-backlog", &polListenBacklog, 10); CFG_GetInt(cf, "ctl.enable", &ctlEnable, 0); CFG_GetInt(cf, "ctl.listen-backlog", &ctlListenBacklog, 10); CFG_GetInt(cf, "sa.mbd-report", &mbdReportEnable, 0); CFG_GetStr(cf, "sa.mbd-host", &mbdHost, "localhost"); CFG_GetStr(cf, "sa.mbd-port", &mbdPort, "925"); CFG_GetStr(cf, "sa.mbd-pass", &mbdPass, "secret"); CFG_GetStr(cf, "sa.force-filter-domains", &forceFilterDomains, ""); CFG_GetFlt(cf, "sa.force-filter-threshold", &forceFilterThreshold, 6.0); /* * Initialization of the subsystems */ if (stat(mpdHome, &sb) != 0 && mkdir(mpdHome, 0755) == -1) { SF_SetError("mkdir %s: %s", mpdHome, strerror(errno)); goto fatal; } if (stat(mpdSocketDir, &sb) != 0 && mkdir(mpdSocketDir, 0755) == -1) { SF_SetError("mkdir %s: %s", mpdSocketDir, strerror(errno)); goto fatal; } if (stat(saUserDir, &sb) != 0 && mkdir(saUserDir, 0755) == -1) { SF_SetError("mkdir %s: %s", saUserDir, strerror(errno)); goto fatal; } if (LOCAL_Init(cf) == -1) { CFG_CloseFile(cf); goto fatal; } if (QMGR_Init(cf) == -1) { LOCAL_Destroy(); CFG_CloseFile(cf); goto fatal; } POL_Init(cf); CTL_Init(cf); ML_Init(cf); CFG_CloseFile(cf); FD_ZERO(&servfds); /* * Set up the LMTP listening socket. */ if (lmtpEnable) { if ((lmtpSock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { syslog(LOG_ERR, "socket(AF_UNIX): %m"); goto out; } unaddr.sun_family = AF_UNIX; Strlcpy(unaddr.sun_path, lmtpSockPath, sizeof(unaddr.sun_path)); unlink(unaddr.sun_path); socklen = SUN_LEN(&unaddr); if (bind(lmtpSock, (struct sockaddr *)&unaddr, socklen) == -1 || listen(lmtpSock, lmtpListenBacklog) == -1) { syslog(LOG_ERR, "%s: %m", unaddr.sun_path); goto out; } chmod(unaddr.sun_path, 0777); FD_SET(lmtpSock, &servfds); if (lmtpSock > maxfd) { maxfd = lmtpSock; } } /* * Set up the SMTP server listening socket(s). */ if (smtpEnable) { memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if ((rv = getaddrinfo(smtpHost, smtpPort, &hints, &res0)) != 0) { syslog(LOG_ERR, "%s:%s: %s", smtpHost, smtpPort, gai_strerror(rv)); goto out; } for (smtpSockCount = 0, res = res0; res != NULL && smtpSockCount < 4; res = res->ai_next) { rv = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (rv == -1) { cause = "socket"; continue; } i = 1; setsockopt(rv, SOL_SOCKET, SO_REUSEADDR, &i, (my_socklen_t)sizeof(i)); if (bind(rv, res->ai_addr, res->ai_addrlen) == -1) { cause = "bind"; close(rv); continue; } if (listen(rv, smtpListenBacklog) == -1) { cause = "listen"; close(rv); continue; } smtpSocks[smtpSockCount++] = rv; FD_SET(rv, &servfds); if (rv > maxfd) { maxfd = rv; } } if (smtpSockCount == 0) { syslog(LOG_ERR, "%s: %m", cause); goto out; } freeaddrinfo(res0); } /* * Set up the Control Interface socket. */ if (ctlEnable) { if ((ctlSock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { syslog(LOG_ERR, "socket(AF_UNIX): %m"); goto out; } unaddr.sun_family = AF_UNIX; Strlcpy(unaddr.sun_path, ctlSockPath, sizeof(unaddr.sun_path)); unlink(unaddr.sun_path); socklen = SUN_LEN(&unaddr); if (bind(ctlSock, (struct sockaddr *)&unaddr, socklen) == -1 || listen(ctlSock, ctlListenBacklog) == -1) { syslog(LOG_ERR, "%s: %m", unaddr.sun_path); goto out; } chmod(unaddr.sun_path, 0777); FD_SET(ctlSock, &servfds); if (ctlSock > maxfd) { maxfd = ctlSock; } } /* * Set up the Policy Server socket. */ if (polEnable) { if ((polSock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { syslog(LOG_ERR, "socket(AF_UNIX): %m"); goto out; } unaddr.sun_family = AF_UNIX; Strlcpy(unaddr.sun_path, polSockPath, sizeof(unaddr.sun_path)); unlink(unaddr.sun_path); socklen = SUN_LEN(&unaddr); if (bind(polSock, (struct sockaddr *)&unaddr, socklen) == -1 || listen(polSock, polListenBacklog) == -1) { syslog(LOG_ERR, "%s: %m", unaddr.sun_path); goto out; } if ((pwd = getpwnam(polOwner)) != NULL) { chown(unaddr.sun_path, pwd->pw_uid, pwd->pw_gid); chmod(unaddr.sun_path, 0700); } else { syslog(LOG_ERR, "Invalid policy owner: %s", polOwner); } FD_SET(polSock, &servfds); if (polSock > maxfd) { maxfd = polSock; } } sigemptyset(&sa.sa_mask); sa.sa_handler = master_sig_chld; sa.sa_flags = SA_RESTART; sigaction(SIGCHLD, &sa, NULL); sa.sa_handler = master_sig_reload; sa.sa_flags = SA_RESTART; sigaction(SIGHUP, &sa, NULL); sa.sa_handler = SIG_IGN; sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); sa.sa_handler = master_sig_die; sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); /* Write the PID file */ if (stat(mpdPidFile, &sb) == 0) { Debug("WARNING: Overwriting existing PID file!"); syslog(LOG_WARNING, "Overwriting %s!", mpdPidFile); } if ((f = fopen(mpdPidFile, "w")) == NULL) { Debug("%s: %s", mpdPidFile, strerror(errno)); syslog(LOG_ERR, "%s: %m", mpdPidFile); goto out_close; } fprintf(f, "%lu\n", (unsigned long)getpid()); fclose(f); /* * Immediately process any entry left in the active queue due * to a previous crash or restart. */ if ((rv = QMGR_Todo()) > 0) QMGR_Cleanup(); /* * Main loop. */ for (;;) { fd_set rfds = servfds; rv = select(maxfd+1, &rfds, NULL, NULL, NULL); if (rv == -1) { if (errno == EINTR) { if (ProcessSignals() == 1) { goto out_close; } continue; } else { syslog(LOG_ERR, "select: %m; shutdown"); exit(1); } } if (ctlEnable && FD_ISSET(ctlSock, &rfds)) { socklen = sizeof(paddr); rv = accept(ctlSock, (struct sockaddr *)&paddr, &socklen); if (rv != -1) { CTL_ForkInstance(rv); close(rv); } else { syslog(LOG_ERR, "accept(CTL): %m"); } } if (polEnable && FD_ISSET(polSock, &rfds)) { socklen = sizeof(paddr); rv = accept(polSock, (struct sockaddr *)&paddr, &socklen); if (rv != -1) { POL_ForkInstance(rv); close(rv); } else { syslog(LOG_ERR, "accept(POL): %m"); } } if (lmtpEnable && FD_ISSET(lmtpSock, &rfds)) { if (lmtpClientCount+1 > lmtpMaxClients) { syslog(LOG_ERR, "Too many LMTP clients!"); goto out_smtp; } socklen = sizeof(paddr); rv = accept(lmtpSock, (struct sockaddr *)&paddr, &socklen); if (rv == -1) { syslog(LOG_ERR, "accept(LMTP): %m"); goto out_lmtp; } if (pipe(pp) == -1) { syslog(LOG_ERR, "pipe(LMTP): %m"); close(rv); goto out_lmtp; } if ((pid = fork()) == -1) { syslog(LOG_ERR, "fork(LMTP): %m"); close(rv); close(pp[0]); close(pp[1]); goto out_lmtp; } else if (pid == 0) { /* Child */ dup2(rv, STDIN_FILENO); dup2(rv, STDOUT_FILENO); close(pp[0]); smtpMasterPipe = pp[1]; SF_EnterServerProc("mailprocd"); fcntl(fileno(stdin), F_SETOWN, getpid()); setvbuf(stdout, NULL, _IOFBF, 0); if (SMTP_Main(LMTP_PROTOCOL) == -1) { syslog(LOG_ERR, "LMTP session: %s", SF_GetError()); } SF_ExitServerProc(0); } else { smtps = Malloc(sizeof(SMTP_Session)); smtps->prot = LMTP_PROTOCOL; smtps->pid = pid; smtps->pipe = pp[0]; FD_SET(smtps->pipe, &servfds); if (smtps->pipe > maxfd) { maxfd = smtps->pipe; } TAILQ_INSERT_TAIL(&smtpSessions, smtps, sessions); lmtpClientCount++; close(pp[1]); close(rv); } } out_lmtp: for (i = 0; i < smtpSockCount; i++) { if (!FD_ISSET(smtpSocks[i], &rfds)) { continue; } if (smtpClientCount+1 > smtpMaxClients) { syslog(LOG_ERR, "Too many SMTP clients!"); goto out_smtp; } socklen = sizeof(paddr); rv = accept(smtpSocks[i], (struct sockaddr *)&paddr, &socklen); if (rv == -1) { syslog(LOG_ERR, "accept(SMTP): %m"); goto out_smtp; } if (pipe(pp) == -1) { syslog(LOG_ERR, "pipe(SMTP): %m"); close(rv); goto out_smtp; } if ((pid = fork()) == -1) { syslog(LOG_ERR, "fork(SMTP): %m"); close(rv); close(pp[0]); close(pp[1]); goto out_lmtp; } else if (pid == 0) { /* Child */ dup2(rv, STDIN_FILENO); dup2(rv, STDOUT_FILENO); close(pp[0]); smtpMasterPipe = pp[1]; SF_EnterServerProc("mailprocd"); fcntl(fileno(stdin), F_SETOWN, getpid()); setvbuf(stdout, NULL, _IOFBF, 0); if (SMTP_Main(ESMTP_PROTOCOL) == -1) { syslog(LOG_ERR, "SMTP session: %s", SF_GetError()); } SF_ExitServerProc(0); } else { smtps = Malloc(sizeof(SMTP_Session)); smtps->prot = ESMTP_PROTOCOL; smtps->pid = pid; smtps->pipe = pp[0]; FD_SET(smtps->pipe, &servfds); if (smtps->pipe > maxfd) { maxfd = smtps->pipe; } TAILQ_INSERT_TAIL(&smtpSessions, smtps, sessions); smtpClientCount++; close(pp[1]); close(rv); } } out_smtp: TAILQ_FOREACH(smtps, &smtpSessions, sessions) { QMGR_MetaData meta; ssize_t nread; if (!FD_ISSET(smtps->pipe, &rfds)) { continue; } for (nread = 0; nread < sizeof(QMGR_MetaData);) { rv = read(smtps->pipe, ((void *)&meta)+nread, (sizeof(QMGR_MetaData) - nread)); if (rv == -1) { if (errno == EINTR) { continue; } else { syslog(LOG_ERR, "Read error on smtps%d: %m", smtps->pid); break; } } else if (rv == 0) { break; } nread += rv; } if (nread < sizeof(QMGR_MetaData)) { continue; } Debug("[MASTER] uid=%d:%d, qid=%s, from=%s, rcpt=%s", meta.uid, meta.gid, meta.qid, meta.from, meta.rcpt); if (ProcessQueueNotification(smtps, &meta) == -1) { syslog(LOG_ERR, "<%s>: FAIL: %s", meta.qid, SF_GetError()); Debug("<%s>: FAIL: %s", meta.qid, SF_GetError()); } } if (ProcessSignals() == 1) break; } out_close: syslog(LOG_NOTICE, "Server shutdown (signal)"); CloseFiles(); ML_Destroy(); CTL_Destroy(); POL_Destroy(); LOCAL_Destroy(); #ifdef HAVE_SA SPAM_Destroy(); #endif unlink(mpdPidFile); out: #ifdef HAVE_PERL perl_destruct(sfPerl); perl_free(sfPerl); PERL_SYS_TERM(); #endif return (0); fatal: fprintf(stderr, "FATAL: %s\n", SF_GetError()); syslog(LOG_CRIT, "FATAL: %s", SF_GetError()); return (1); }