diff options
Diffstat (limited to 'usr.sbin/popa3d/standalone.c')
-rw-r--r-- | usr.sbin/popa3d/standalone.c | 339 |
1 files changed, 0 insertions, 339 deletions
diff --git a/usr.sbin/popa3d/standalone.c b/usr.sbin/popa3d/standalone.c deleted file mode 100644 index b3be2844793..00000000000 --- a/usr.sbin/popa3d/standalone.c +++ /dev/null @@ -1,339 +0,0 @@ -/* $OpenBSD: standalone.c,v 1.14 2009/11/12 11:03:37 jsg Exp $ */ - -/* - * Standalone POP server: accepts connections, checks the anti-flood limits, - * logs and starts the actual POP sessions. - */ - -#include "params.h" - -#if POP_STANDALONE - -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <signal.h> -#include <syslog.h> -#include <time.h> -#include <errno.h> -#include <netdb.h> -#include <poll.h> -#include <sys/times.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#if DAEMON_LIBWRAP -#include <tcpd.h> -int allow_severity = SYSLOG_PRI_LO; -int deny_severity = SYSLOG_PRI_HI; -#endif - -/* - * These are defined in pop_root.c. - */ -extern int log_error(char *s); -extern int do_pop_startup(void); -extern int do_pop_session(void); -extern int af; - -typedef volatile sig_atomic_t va_int; - -/* - * Active POP sessions. Those that were started within the last MIN_DELAY - * seconds are also considered active (regardless of their actual state), - * to allow for limiting the logging rate without throwing away critical - * information about sessions that we could have allowed to proceed. - */ -static struct { - char addr[NI_MAXHOST]; /* Source IP address */ - va_int pid; /* PID of the server, or 0 for none */ - clock_t start; /* When the server was started */ - clock_t log; /* When we've last logged a failure */ -} sessions[MAX_SESSIONS]; - -static va_int child_blocked; /* We use blocking to avoid races */ -static va_int child_pending; /* Are any dead children waiting? */ - -int handle(int); - -/* - * SIGCHLD handler. - */ -static void handle_child(int signum) -{ - int saved_errno; - pid_t pid; - int i; - - saved_errno = errno; - - if (child_blocked) - child_pending = 1; - else { - child_pending = 0; - - while ((pid = waitpid(0, NULL, WNOHANG)) > 0) - for (i = 0; i < MAX_SESSIONS; i++) - if (sessions[i].pid == pid) { - sessions[i].pid = 0; - break; - } - } - - signal(SIGCHLD, handle_child); - - errno = saved_errno; -} - -#if DAEMON_LIBWRAP -static void check_access(int sock) -{ - struct request_info request; - - request_init(&request, - RQ_DAEMON, DAEMON_LIBWRAP_IDENT, - RQ_FILE, sock, - 0); - fromhost(&request); - - if (!hosts_access(&request)) { -/* refuse() shouldn't return... */ - refuse(&request); -/* ...but just in case */ - exit(1); - } -} -#endif - -#if POP_OPTIONS -int do_standalone(void) -#else -int main(void) -#endif -{ - int error, i, n, true = 1; - struct pollfd *pfds; - struct addrinfo hints, *res, *res0; - char sbuf[NI_MAXSERV]; - - if (do_pop_startup()) return 1; - - snprintf(sbuf, sizeof(sbuf), "%u", DAEMON_PORT); - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = af; - hints.ai_flags = AI_PASSIVE; - error = getaddrinfo(NULL, sbuf, &hints, &res0); - if (error) - return log_error("getaddrinfo"); - - i = 0; - for (res = res0; res; res = res->ai_next) - i++; - - pfds = calloc(i, sizeof(pfds[0])); - if (!pfds) { - freeaddrinfo(res0); - return log_error("malloc"); - } - - i = 0; - for (res = res0; res; res = res->ai_next) { - if ((pfds[i].fd = socket(res->ai_family, res->ai_socktype, - res->ai_protocol)) < 0) - continue; - - if (setsockopt(pfds[i].fd, SOL_SOCKET, SO_REUSEADDR, - (void *)&true, sizeof(true))) { - close(pfds[i].fd); - continue; - } - -#ifdef IPV6_V6ONLY - if (res->ai_family == AF_INET6) - (void)setsockopt(pfds[i].fd, IPPROTO_IPV6, IPV6_V6ONLY, - (void *)&true, sizeof(true)); -#endif - - if (bind(pfds[i].fd, res->ai_addr, res->ai_addrlen)) { - close(pfds[i].fd); - continue; - } - - if (listen(pfds[i].fd, MAX_BACKLOG)) { - close(pfds[i].fd); - continue; - } - - pfds[i].events = POLLIN; - i++; - } - freeaddrinfo(res0); - - if (i == 0) { - free(pfds); - return log_error("socket"); - } - - n = i; - - chdir("/"); - setsid(); - - switch (fork()) { - case -1: - free(pfds); - return log_error("fork"); - - case 0: - break; - - default: - free(pfds); - return 0; - } - - setsid(); - - child_blocked = 1; - child_pending = 0; - signal(SIGCHLD, handle_child); - - memset((void *)sessions, 0, sizeof(sessions)); - - while (1) { - child_blocked = 0; - if (child_pending) raise(SIGCHLD); - - i = poll(pfds, n, INFTIM); - if (i < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - free(pfds); - return log_error("poll"); - } - - for (i = 0; i < n; i++) - if (pfds[i].revents & POLLIN) - handle(pfds[i].fd); - } -} - -int -handle(int sock) -{ - clock_t now, log; - int new; - char hbuf[NI_MAXHOST]; - struct sockaddr_storage addr; - socklen_t addrlen; - pid_t pid; - struct tms buf; - int error; - int j, n, i, s; - - log = 0; - new = 0; - - addrlen = sizeof(addr); - new = accept(sock, (struct sockaddr *)&addr, &addrlen); -/* - * I wish there was a portable way to classify errno's... In this case, - * it appears to be better to risk eating up the CPU on a fatal error - * rather than risk terminating the entire service because of a minor - * temporary error having to do with one particular connection attempt. - */ - if (new < 0) - return -1; - - error = getnameinfo((struct sockaddr *)&addr, addrlen, - hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); - if (error) { - syslog(SYSLOG_PRI_HI, - "could not get host address"); - close(new); - return -1; - } - - now = times(&buf); - if (!now) - now = 1; - - child_blocked = 1; - - j = -1; - n = 0; - s = 0; - for (i = 0; i < MAX_SESSIONS; i++) { - s = i; - if (sessions[i].start > now) - sessions[i].start = 0; - if (sessions[i].pid || - (sessions[i].start && - now - sessions[i].start < MIN_DELAY * CLK_TCK)) { - if (strcmp(sessions[i].addr, hbuf) == 0) - if (++n >= MAX_SESSIONS_PER_SOURCE) - break; - } else if (j < 0) - j = i; - } - - if (n >= MAX_SESSIONS_PER_SOURCE) { - if (!sessions[s].log || - now < sessions[s].log || - now - sessions[s].log >= MIN_DELAY * CLK_TCK) { - syslog(SYSLOG_PRI_HI, - "%s: per source limit reached", - hbuf); - sessions[s].log = now; - } - close(new); - return -1; - } - - if (j < 0) { - if (!log || - now < log || now - log >= MIN_DELAY * CLK_TCK) { - syslog(SYSLOG_PRI_HI, - "%s: sessions limit reached", hbuf); - log = now; - } - close(new); - return -1; - } - - switch ((pid = fork())) { - case -1: - syslog(SYSLOG_PRI_ERROR, "%s: fork: %m", hbuf); - close(new); - return -1; - - case 0: -#if DAEMON_LIBWRAP - check_access(new); -#endif - syslog(SYSLOG_PRI_LO, "Session from %s", - hbuf); - if (dup2(new, 0) < 0 || dup2(new, 1) < 0 || dup2(new, 2) < 0) { - log_error("dup2"); - _exit(1); - } - closefrom(3); - _exit(do_pop_session()); - - default: - close(new); - strlcpy(sessions[j].addr, hbuf, - sizeof(sessions[j].addr)); - sessions[j].pid = (va_int)pid; - sessions[j].start = now; - sessions[j].log = 0; - return 0; - } -} - -#endif |