diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2004-06-20 20:46:28 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2004-06-20 20:46:28 +0000 |
commit | 08654bfab44aaf6923b0054d764ae829837ae77b (patch) | |
tree | 01b1886a367b84e9a76539fa124b1471a545b591 | |
parent | 0ab9c5f278e14a9c130f18ce6b6aae88a097f7ee (diff) |
IPv4/v6 support for standalone mode. brad tested and ok'ed
-rw-r--r-- | usr.sbin/popa3d/popa3d.8 | 12 | ||||
-rw-r--r-- | usr.sbin/popa3d/standalone.c | 269 | ||||
-rw-r--r-- | usr.sbin/popa3d/startup.c | 16 |
3 files changed, 198 insertions, 99 deletions
diff --git a/usr.sbin/popa3d/popa3d.8 b/usr.sbin/popa3d/popa3d.8 index c7706d3d1eb..8ecab7e0dd1 100644 --- a/usr.sbin/popa3d/popa3d.8 +++ b/usr.sbin/popa3d/popa3d.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: popa3d.8,v 1.8 2003/06/12 12:59:52 jmc Exp $ +.\" $OpenBSD: popa3d.8,v 1.9 2004/06/20 20:46:27 itojun Exp $ .\" .\" Copyright (c) 2001-2003 Camiel Dobbelaar (cd@sentia.nl) .\" All rights reserved. @@ -32,7 +32,7 @@ .Nd "Post Office Protocol (POP3) server" .Sh SYNOPSIS .Nm -.Op Fl D | V +.Op Fl DV46 .Sh DESCRIPTION .Nm is a POP3 server. @@ -77,6 +77,14 @@ also does quite a few checks to significantly reduce the impact of connection flood attacks. .It Fl V Show version information and exit. +.It Fl 4 +In standalone mode +.Pq Fl D , +listen to IPv4 only. +.It Fl 6 +In standalone mode +.Pq Fl D , +listen to IPv6 only. .Pp .El .Pp diff --git a/usr.sbin/popa3d/standalone.c b/usr.sbin/popa3d/standalone.c index e04e606f720..009373718ee 100644 --- a/usr.sbin/popa3d/standalone.c +++ b/usr.sbin/popa3d/standalone.c @@ -1,4 +1,4 @@ -/* $OpenBSD: standalone.c,v 1.5 2003/05/12 19:28:22 camield Exp $ */ +/* $OpenBSD: standalone.c,v 1.6 2004/06/20 20:46:27 itojun Exp $ */ /* * Standalone POP server: accepts connections, checks the anti-flood limits, @@ -17,6 +17,8 @@ #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> @@ -36,6 +38,7 @@ int deny_severity = SYSLOG_PRI_HI; 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; @@ -46,7 +49,7 @@ typedef volatile sig_atomic_t va_int; * information about sessions that we could have allowed to proceed. */ static struct { - struct in_addr addr; /* Source IP address */ + char addr[NI_MAXHOST]; /* Source IP address */ volatile 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 */ @@ -55,13 +58,15 @@ static struct { 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 pid; int i; saved_errno = errno; @@ -72,11 +77,11 @@ static void handle_child(int signum) 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; - } + for (i = 0; i < MAX_SESSIONS; i++) + if (sessions[i].pid == pid) { + sessions[i].pid = 0; + break; + } } signal(SIGCHLD, handle_child); @@ -111,32 +116,75 @@ int main(void) #endif { int true = 1; - int sock, new; - struct sockaddr_in addr; - socklen_t addrlen; - pid_t pid; - struct tms buf; - clock_t now, log; - int i, j, n; + int *fds, new, sock; + struct pollfd *pfds; + int i, n; + struct addrinfo hints, *res, *res0; + char sbuf[NI_MAXSERV]; + int error; if (do_pop_startup()) return 1; - if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) - return log_error("socket"); + 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++; + + fds = malloc(i * sizeof(fds[0])); + if (!fds) + return log_error("malloc"); + pfds = malloc(i * sizeof(pfds[0])); + if (!pfds) + return log_error("malloc"); + + i = 0; + for (res = res0; res; res = res->ai_next) { + if ((fds[i] = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) < 0) + continue; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (void *)&true, sizeof(true))) - return log_error("setsockopt"); + if (setsockopt(fds[i], SOL_SOCKET, SO_REUSEADDR, + (void *)&true, sizeof(true))) { + close(fds[i]); + continue; + } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(DAEMON_ADDR); - addr.sin_port = htons(DAEMON_PORT); - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) - return log_error("bind"); +#ifdef IPV6_V6ONLY + if (res->ai_family == AF_INET6) + (void)setsockopt(fds[i], IPPROTO_IPV6, IPV6_V6ONLY, + (void *)&true, sizeof(true)); +#endif - if (listen(sock, MAX_BACKLOG)) - return log_error("listen"); + if (bind(fds[i], res->ai_addr, res->ai_addrlen)) { + close(fds[i]); + continue; + } + + if (listen(fds[i], MAX_BACKLOG)) { + close(fds[i]); + continue; + } + + memset(&pfds[i], 0, sizeof(pfds[i])); + pfds[i].fd = fds[i]; + pfds[i].events = POLLIN; + + i++; + } + freeaddrinfo(res0); + + if (i == 0) + return log_error("socket"); + + n = i; chdir("/"); setsid(); @@ -159,19 +207,49 @@ int main(void) signal(SIGCHLD, handle_child); memset((void *)sessions, 0, sizeof(sessions)); - log = 0; new = 0; while (1) { child_blocked = 0; - if (child_pending) raise(SIGCHLD); + if (child_pending) + raise(SIGCHLD); - if (new > 0) - if (close(new)) return log_error("close"); + i = poll(pfds, n, INFTIM); + + if (i < 0) + log_error("poll"); + + sock = -1; + 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; + int addrlen; + int pid; + struct tms buf; + int error; + int j, n, i; + + log = 0; + new = 0; - addrlen = sizeof(addr); - new = accept(sock, (struct sockaddr *)&addr, &addrlen); + addrlen = sizeof(addr); + new = accept(sock, (struct sockaddr *)&addr, &addrlen); + + error = getnameinfo((struct sockaddr *)&addr, addrlen, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + if (error) + ; /* XXX */ /* * I wish there was a portable way to classify errno's... In this case, @@ -179,75 +257,76 @@ int main(void) * rather than risk terminating the entire service because of a minor * temporary error having to do with one particular connection attempt. */ - if (new < 0) continue; - - now = times(&buf); - if (!now) now = 1; - - child_blocked = 1; - - j = -1; n = 0; - for (i = 0; i < MAX_SESSIONS; 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 (sessions[i].addr.s_addr == - addr.sin_addr.s_addr) - if (++n >= MAX_SESSIONS_PER_SOURCE) break; - } else - if (j < 0) j = i; - } + if (new < 0) + return 0; - if (n >= MAX_SESSIONS_PER_SOURCE) { - if (!sessions[i].log || - now < sessions[i].log || - now - sessions[i].log >= MIN_DELAY * CLK_TCK) { - syslog(SYSLOG_PRI_HI, - "%s: per source limit reached", - inet_ntoa(addr.sin_addr)); - sessions[i].log = now; - } - continue; + now = times(&buf); + if (!now) + now = 1; + + child_blocked = 1; + + j = -1; + n = 0; + for (i = 0; i < MAX_SESSIONS; 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[i].log || + now < sessions[i].log || + now - sessions[i].log >= MIN_DELAY * CLK_TCK) { + syslog(SYSLOG_PRI_HI, + "%s: per source limit reached", + hbuf); + sessions[i].log = now; } + return 0; + } - if (j < 0) { - if (!log || - now < log || now - log >= MIN_DELAY * CLK_TCK) { - syslog(SYSLOG_PRI_HI, - "%s: sessions limit reached", - inet_ntoa(addr.sin_addr)); - log = now; - } - continue; + if (j < 0) { + if (!log || + now < log || now - log >= MIN_DELAY * CLK_TCK) { + syslog(SYSLOG_PRI_HI, + "%s: sessions limit reached", hbuf); + log = now; } + return 0; + } - switch ((pid = fork())) { - case -1: - syslog(SYSLOG_PRI_ERROR, "%s: fork: %m", - inet_ntoa(addr.sin_addr)); - break; + switch ((pid = fork())) { + case -1: + syslog(SYSLOG_PRI_ERROR, "%s: fork: %m", hbuf); + break; - case 0: - if (close(sock)) return log_error("close"); + case 0: + if (close(sock)) return log_error("close"); #if DAEMON_LIBWRAP - check_access(new); + check_access(new); #endif - syslog(SYSLOG_PRI_LO, "Session from %s", - inet_ntoa(addr.sin_addr)); - if (dup2(new, 0) < 0) return log_error("dup2"); - if (dup2(new, 1) < 0) return log_error("dup2"); - if (dup2(new, 2) < 0) return log_error("dup2"); - if (close(new)) return log_error("close"); - return do_pop_session(); - - default: - sessions[j].addr = addr.sin_addr; - sessions[j].pid = pid; - sessions[j].start = now; - sessions[j].log = 0; - } + syslog(SYSLOG_PRI_LO, "Session from %s", + hbuf); + if (dup2(new, 0) < 0) return log_error("dup2"); + if (dup2(new, 1) < 0) return log_error("dup2"); + if (dup2(new, 2) < 0) return log_error("dup2"); + if (close(new)) return log_error("close"); + return do_pop_session(); + + default: + strlcpy(sessions[j].addr, hbuf, + sizeof(sessions[j].addr)); + (va_int)sessions[j].pid = pid; + sessions[j].start = now; + sessions[j].log = 0; } } diff --git a/usr.sbin/popa3d/startup.c b/usr.sbin/popa3d/startup.c index 5d66fee8160..6b93e36a909 100644 --- a/usr.sbin/popa3d/startup.c +++ b/usr.sbin/popa3d/startup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: startup.c,v 1.2 2003/05/12 19:28:22 camield Exp $ */ +/* $OpenBSD: startup.c,v 1.3 2004/06/20 20:46:27 itojun Exp $ */ /* * Command line option parsing. @@ -8,6 +8,8 @@ #if POP_OPTIONS +#include <sys/types.h> +#include <sys/socket.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -30,6 +32,8 @@ extern char *__progname; static char *progname; #endif +int af = PF_UNSPEC; + static void usage(void) { fprintf(stderr, "Usage: %s [-D] [-V]\n", progname); @@ -52,12 +56,20 @@ int main(int argc, char **argv) progname = POP_SERVER; #endif - while ((c = getopt(argc, argv, "DV")) != -1) { + while ((c = getopt(argc, argv, "DV46")) != -1) { switch (c) { case 'D': standalone++; break; + case '4': + af = AF_INET; + break; + + case '6': + af = AF_INET6; + break; + case 'V': version(); |