summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2004-06-20 20:46:28 +0000
committerJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2004-06-20 20:46:28 +0000
commit08654bfab44aaf6923b0054d764ae829837ae77b (patch)
tree01b1886a367b84e9a76539fa124b1471a545b591 /usr.sbin
parent0ab9c5f278e14a9c130f18ce6b6aae88a097f7ee (diff)
IPv4/v6 support for standalone mode. brad tested and ok'ed
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/popa3d/popa3d.812
-rw-r--r--usr.sbin/popa3d/standalone.c269
-rw-r--r--usr.sbin/popa3d/startup.c16
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();