diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2004-07-20 17:07:35 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2004-07-20 17:07:35 +0000 |
commit | d72d974d64e1d28e141b4b1ec0899eb48e58eca3 (patch) | |
tree | e9cfbf5a70ee10735e00daae36a57ea86fd7de90 /usr.sbin/popa3d | |
parent | c0994a25cb3e771d8ee589fb60bf9f35815eda76 (diff) |
IPv6 support originally by itojun@ with standalone mode fixes by me.
Tested by several people in both inetd and standalone mode w/ IPv4 and IPv6.
Diffstat (limited to 'usr.sbin/popa3d')
-rw-r--r-- | usr.sbin/popa3d/popa3d.8 | 13 | ||||
-rw-r--r-- | usr.sbin/popa3d/standalone.c | 256 | ||||
-rw-r--r-- | usr.sbin/popa3d/startup.c | 18 |
3 files changed, 190 insertions, 97 deletions
diff --git a/usr.sbin/popa3d/popa3d.8 b/usr.sbin/popa3d/popa3d.8 index 61389528de9..9d8d59a6c3d 100644 --- a/usr.sbin/popa3d/popa3d.8 +++ b/usr.sbin/popa3d/popa3d.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: popa3d.8,v 1.11 2004/07/17 20:54:24 brad Exp $ +.\" $OpenBSD: popa3d.8,v 1.12 2004/07/20 17:07:34 millert 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 46DV .Sh DESCRIPTION .Nm is a POP3 server. @@ -60,6 +60,14 @@ To send mail, the SMTP protocol is commonly used; see .Pp The options are as follows: .Bl -tag -width Ds +.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. .It Fl D With this option set, .Nm @@ -77,7 +85,6 @@ also does quite a few checks to significantly reduce the impact of connection flood attacks. .It Fl V Show version information and exit. -.Pp .El .Pp Alternatively, diff --git a/usr.sbin/popa3d/standalone.c b/usr.sbin/popa3d/standalone.c index 2d146dd5b84..0e9d6ba2d1e 100644 --- a/usr.sbin/popa3d/standalone.c +++ b/usr.sbin/popa3d/standalone.c @@ -1,4 +1,4 @@ -/* $OpenBSD: standalone.c,v 1.7 2004/07/17 20:54:24 brad Exp $ */ +/* $OpenBSD: standalone.c,v 1.8 2004/07/20 17:07:34 millert 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,8 +49,8 @@ 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 */ - volatile int pid; /* PID of the server, or 0 for none */ + 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]; @@ -55,6 +58,8 @@ 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. */ @@ -110,33 +115,67 @@ int do_standalone(void) 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 error, i, n, true = 1; + struct pollfd *pfds; + struct addrinfo hints, *res, *res0; + char sbuf[NI_MAXSERV]; 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++; + + pfds = calloc(i, sizeof(pfds[0])); + if (!pfds) + 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; + pfds[i].events = POLLIN; + + 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 (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (void *)&true, sizeof(true))) - return log_error("setsockopt"); + if (listen(pfds[i].fd, MAX_BACKLOG)) { + close(pfds[i].fd); + 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"); + i++; + } + freeaddrinfo(res0); - if (listen(sock, MAX_BACKLOG)) - return log_error("listen"); + if (i == 0) + return log_error("socket"); + + n = i; chdir("/"); setsid(); @@ -159,95 +198,130 @@ 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 (new > 0) - if (close(new)) return log_error("close"); + i = poll(pfds, n, INFTIM); + if (i < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return log_error("poll"); + } - addrlen = sizeof(addr); - new = accept(sock, (struct sockaddr *)&addr, &addrlen); + 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; + pid_t pid; + struct tms buf; + int error; + int j, n, i; + + 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) 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 -1; + + error = getnameinfo((struct sockaddr *)&addr, addrlen, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + if (error) { + syslog(SYSLOG_PRI_HI, + "%s: invalid IP address", hbuf); + return -1; + } - 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; } + 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", - 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; } + close(new); + return -1; + } - 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 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 || 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; } } diff --git a/usr.sbin/popa3d/startup.c b/usr.sbin/popa3d/startup.c index fc25a777b3b..88e9b9ade5e 100644 --- a/usr.sbin/popa3d/startup.c +++ b/usr.sbin/popa3d/startup.c @@ -1,4 +1,4 @@ -/* $OpenBSD: startup.c,v 1.5 2004/07/17 20:54:24 brad Exp $ */ +/* $OpenBSD: startup.c,v 1.6 2004/07/20 17:07:34 millert 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,9 +32,11 @@ extern char *__progname; static char *progname; #endif +int af = PF_UNSPEC; + static void usage(void) { - fprintf(stderr, "Usage: %s [-D] [-V]\n", progname); + fprintf(stderr, "Usage: %s [-46DV]\n", progname); exit(1); } @@ -52,8 +56,16 @@ int main(int argc, char **argv) progname = POP_SERVER; #endif - while ((c = getopt(argc, argv, "DV")) != -1) { + while ((c = getopt(argc, argv, "46DV")) != -1) { switch (c) { + case '4': + af = AF_INET; + break; + + case '6': + af = AF_INET6; + break; + case 'D': standalone++; break; |