summaryrefslogtreecommitdiff
path: root/usr.sbin/popa3d
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/popa3d')
-rw-r--r--usr.sbin/popa3d/popa3d.813
-rw-r--r--usr.sbin/popa3d/standalone.c256
-rw-r--r--usr.sbin/popa3d/startup.c18
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;