summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Beck <beck@cvs.openbsd.org>2003-03-02 19:22:01 +0000
committerBob Beck <beck@cvs.openbsd.org>2003-03-02 19:22:01 +0000
commit866a15ac7401268d7e7ba88ce2463acc2a9f0794 (patch)
tree88b514fe30b236cde34a4bb432766687e29de0ed
parent641d06a264defa4e03e7fb9c5a2ac06d231e6eed (diff)
Spamd changes to add blacklist awareness to spamd, new spamd-setup.pl
which configures individual blacklists sources and deals with whitelists. Perl still needs some stylistic changes as suggested by bmc which will go in shortly. ok deraadt@
-rw-r--r--libexec/spamd/Makefile8
-rw-r--r--libexec/spamd/sdl.c233
-rw-r--r--libexec/spamd/sdl.h71
-rw-r--r--libexec/spamd/spamd-setup.898
-rw-r--r--libexec/spamd/spamd-setup.pl310
-rw-r--r--libexec/spamd/spamd.868
-rw-r--r--libexec/spamd/spamd.c437
7 files changed, 1175 insertions, 50 deletions
diff --git a/libexec/spamd/Makefile b/libexec/spamd/Makefile
index ac9da0c2c60..b65d71caf91 100644
--- a/libexec/spamd/Makefile
+++ b/libexec/spamd/Makefile
@@ -1,13 +1,13 @@
-# $OpenBSD: Makefile,v 1.2 2003/02/14 05:32:02 jason Exp $
+# $OpenBSD: Makefile,v 1.3 2003/03/02 19:21:59 beck Exp $
PROG= spamd
-SRCS= spamd.c
+SRCS= spamd.c sdl.c
MAN= spamd.8 spamd-setup.8
-CFLAGS+= -Wall -Werror
+CFLAGS+= -Wall -Werror
afterinstall:
${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
- ${.CURDIR}/spamd-setup.sh ${DESTDIR}${BINDIR}/spamd-setup
+ ${.CURDIR}/spamd-setup.pl ${DESTDIR}${BINDIR}/spamd-setup
.include <bsd.prog.mk>
diff --git a/libexec/spamd/sdl.c b/libexec/spamd/sdl.c
new file mode 100644
index 00000000000..811aa320be7
--- /dev/null
+++ b/libexec/spamd/sdl.c
@@ -0,0 +1,233 @@
+/* $OpenBSD: sdl.c,v 1.1 2003/03/02 19:22:00 beck Exp $ */
+/*
+ * Copyright (c) 2003 Bob Beck. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * sdl.c - Implement spamd source lists
+ *
+ * This consists of everything we need to do to determine which lists
+ * someone is on. Spamd gets the connecting address, and looks it up
+ * against all lists to determine what deferral messages to feed back
+ * to the connecting machine. - The redirection to spamd will happen
+ * from pf in the kernel, first macth will rdr to us. Spamd (along with
+ * setup) must keep track of *all* matches, so as to tell someone all the
+ * lists that they are on.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sdl.h"
+
+extern int debug;
+struct sdlist *blacklists = NULL;
+int blc = 0, blu = 0;
+
+int
+sdl_add (char *sdname, char *sdstring, char ** addrs, int addrc)
+{
+ int i, index = -1;
+ char astring[40];
+ unsigned int maskbits;
+ struct sdaddr *m, *n;
+
+ /*
+ * if a blacklist of same tag name is already there, replace it,
+ * otherwise append.
+ */
+
+ for (i = 0; i < blu; i++)
+ if (strcmp(blacklists[i].tag, sdname) == 0)
+ index = i;
+ if (index != -1) {
+ if (debug > 0)
+ printf("replacing list %s\n", blacklists[index].tag);
+ free(blacklists[index].tag);
+ free(blacklists[index].string);
+ free(blacklists[index].addrs);
+ } else {
+ if (debug > 0)
+ printf("adding list %s\n", sdname);
+ index = blu;
+ }
+ if (index == blu && blu == blc) {
+ struct sdlist *tmp;
+ tmp = realloc (blacklists, (blc + 128) *
+ sizeof(struct sdlist));
+ if (tmp == NULL)
+ return (-1);
+ blacklists = tmp;
+ blc += 128;
+ }
+ blacklists[index].tag = strdup(sdname);
+ blacklists[index].string = strdup(sdstring);
+ blacklists[index].naddrs = addrc;
+
+ /* cycle through addrs, converting. We assume they are correctly
+ * formatted v4 and v6 addrs, if they don't all convert correcly, the
+ * add fails. Each address should be address/maskbits
+ */
+
+ blacklists[index].addrs = malloc(addrc * sizeof(struct sdentry));
+ if (blacklists[index].addrs == NULL)
+ return(-1);
+
+ for(i = 0; i < addrc; i++) {
+ int j, k, af;
+ n = &blacklists[index].addrs[i].sda;
+ m = &blacklists[index].addrs[i].sdm;
+
+ j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits);
+ if (j != 2)
+ goto parse_error;
+ if (maskbits > 128)
+ goto parse_error;
+ /* sanity check! we don't allow a 0 mask -
+ * don't blacklist the entire net.
+ */
+ if (maskbits == 0)
+ goto parse_error;
+ if (strchr(astring, ':') != NULL)
+ af = AF_INET6;
+ else
+ af = AF_INET;
+ if (af == AF_INET && maskbits > 32)
+ goto parse_error;
+ j = inet_pton(af, astring, n);
+ if (j != 1)
+ goto parse_error;
+ if (debug > 0)
+ printf("added %s/%u\n", astring, maskbits);
+
+ /* set mask, borrowed from pf */
+ k = 0;
+ for (j = 0; j < 4; j++)
+ m->addr32[j] = 0;
+ while (maskbits >= 32) {
+ m->addr32[k++] = 0xffffffff;
+ maskbits -= 32;
+ }
+ for (j = 31; j > 31 - maskbits; --j)
+ m->addr32[k] |= (1 << j);
+ if (maskbits)
+ m->addr32[k] = htonl(m->addr32[k]);
+
+ /* mask off address bits that won't ever be used */
+ for (j = 0; j < 4; j++)
+ n->addr32[j] = n->addr32[j] & m->addr32[j];
+ }
+ if (index == blu) {
+ blu++;
+ blacklists[blu].tag = NULL;
+ }
+ return (0);
+ parse_error:
+ if (debug > 0)
+ printf("sdl_add: parse error, \"%s\"\n", astring);
+ return(-1);
+}
+
+
+/*
+ * Return 1 if the addresses a (with mask m) matches address b
+ * otherwise return 0. It is assumed that address a has been
+ * pre-masked out, we only need to mask b.
+ */
+int
+match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
+ sa_family_t af)
+{
+ int match = 0;
+
+ switch (af) {
+ case AF_INET:
+ if ((a->addr32[0]) ==
+ (b->addr32[0] & m->addr32[0]))
+ match++;
+ break;
+ case AF_INET6:
+ if (((a->addr32[0]) ==
+ (b->addr32[0] & m->addr32[0])) &&
+ ((a->addr32[1]) ==
+ (b->addr32[1] & m->addr32[1])) &&
+ ((a->addr32[2]) ==
+ (b->addr32[2] & m->addr32[2])) &&
+ ((a->addr32[3]) ==
+ (b->addr32[3] & m->addr32[3])))
+ match++;
+ break;
+ }
+ return (match);
+}
+
+
+/*
+ * Given an address and address family
+ * return list of pointers to matching nodes. or NULL if none.
+ */
+struct sdlist **
+sdl_lookup(struct sdlist *head, int af, void * src)
+{
+ int i, matches = 0;
+ struct sdlist *sdl;
+ struct sdentry *sda;
+ struct sdaddr *source = (struct sdaddr *) src ;
+ static int sdnewlen = 0;
+ static struct sdlist **sdnew = NULL;
+
+ if (head == NULL)
+ return (NULL);
+ else
+ sdl = head;
+ while (sdl->tag != NULL) {
+ for (i = 0; i < sdl->naddrs; i++) {
+ sda = sdl->addrs + i;
+ if (match_addr(&sda->sda, &sda->sdm, source, af)) {
+ if (matches == sdnewlen) {
+ struct sdlist **tmp;
+ tmp = realloc(sdnew,
+ (sdnewlen + 128) *
+ sizeof(struct sdlist *) );
+ if (tmp == NULL)
+ /* XXX out of memory - return
+ what we have */
+ return(sdnew);
+ sdnew = tmp;
+ sdnewlen += 128;
+ }
+ sdnew[matches]= sdl;
+ matches++;
+ sdnew[matches]=NULL;
+ break;
+ }
+ }
+ sdl++;
+ }
+ return(sdnew);
+}
diff --git a/libexec/spamd/sdl.h b/libexec/spamd/sdl.h
new file mode 100644
index 00000000000..795f1cc6795
--- /dev/null
+++ b/libexec/spamd/sdl.h
@@ -0,0 +1,71 @@
+/* $OpenBSD: sdl.h,v 1.1 2003/03/02 19:22:00 beck Exp $ */
+/*
+ * Copyright (c) 2003 Bob Beck, Kjell Wooding. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SDL_H_
+#define _SDL_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/ip_ipsp.h>
+
+/* structs */
+
+struct sdlist { /* spamd source list */
+ char *tag; /* sdlist source name */
+ char *string; /* Format (451) string with no smtp code or \r\n */
+ struct sdentry *addrs;
+ size_t naddrs;
+};
+
+/* yeah. Stolen from pf */
+struct sdaddr {
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ u_int8_t addr8[16];
+ u_int16_t addr16[8];
+ u_int32_t addr32[4];
+ } _sda; /* 128-bit address */
+#define v4 _sda.v4
+#define v6 _sda.v6
+#define addr8 _sda.addr8
+#define addr16 _sda.addr16
+#define addr32 _sda.addr32
+};
+
+struct sdentry { /* spamd netblock (black) list */
+ struct sdaddr sda;
+ struct sdaddr sdm;
+};
+
+
+/* prototypes */
+
+extern int sdl_add(char *, char *, char **, int);
+extern struct sdlist **
+sdl_lookup(struct sdlist *head, int af, void * src);
+
+
+#endif /* _SDL_H_ */
diff --git a/libexec/spamd/spamd-setup.8 b/libexec/spamd/spamd-setup.8
index fc3f0936e70..9265961d02c 100644
--- a/libexec/spamd/spamd-setup.8
+++ b/libexec/spamd/spamd-setup.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: spamd-setup.8,v 1.3 2003/02/19 19:18:54 jason Exp $
+.\" $OpenBSD: spamd-setup.8,v 1.4 2003/03/02 19:22:00 beck Exp $
.\"
.\" Copyright (c) 2003 Jason L. Wright (jason@thought.net)
.\" All rights reserved.
@@ -36,39 +36,60 @@
.Nd parse and load file of spammer addresses
.Sh SYNOPSIS
.Nm spamd-setup
+.Op Fl c
+.Op Fl s
.Op Fl s
.Op Fl 1
.Op Fl 2
.Op Fl f Ar file
.Op Fl w Ar file
+.Op Ar file ...
.Sh DESCRIPTION
The
.Nm
-utility adds or deletes address from the
+utility adds blacklists by adding addresses to the
.Xr pf 4
table
-.Aq spamd .
-This table combined with
-.Xr spamd 8
-and a
+.Aq spamd ,
+as well as configuring mail rejection messages for
+the added list of addresses in
+.Xr spamd 8 .
+The
+.Aq spamd
+table is used in conjuction with a
.Xr pf 4
-redirection rule can be used to selectively send spammers
+redirection rule can be used to selectively redirect mail connections
+to the
.Xr spamd 8 .
+daemon .
Sources and actions are as follows:
.Bl -tag -width XXXXXXXXXX
.It Fl s
The SPEWS level 1 database is fetched via
.Xr ftp 1
-and used in the black-list.
+and used in a blacklist named
+.Li spews-1
.It Fl 1
Synonym for
.Fl s .
.It Fl 2
The SPEWS level 2 database is fetched via
.Xr ftp 1
-and used in the black-list.
+and used in a blacklist named
+.Li spews-2
+.It Fl c
+The chinese netblock datbase is fetched via
+.Xr ftp 1
+and used in a blacklist named
+.Li china
+.It Fl k
+The korean netblock database is fetched via
+.Xr ftp 1
+and used in a blacklist named
+.Li korea
.It Fl f Ar file
-The local file specified is added to the black-list.
+The local file specified is used in a black-list named
+.Li local
.It Fl w Ar file
The local file specified is added to the white-list.
.El
@@ -78,11 +99,64 @@ output is concatenated to build up a table for
.Xr pf 4 .
First, all of the addresses from the black-list are added.
Then, all of the addresses from the white-list are removed.
-The input file is expected to consist of one IP address per line (optionally
-followed by a space and text that is ignored).
+Then, the blacklist address, are sent to a running
+.Xr spamd 8
+along with the message spamd will give on mail rejection when
+a matching client connects.
+The input file is expected to consist of one network block or address
+ per line (optionally followed by a space and text that is ignored).
Comment lines beginning with
.Li #
are ignored.
+Network blocks may be specified in any of the formats as in
+the following example:
+.Bd -literal -offset indent
+.Ic # CIDR format
+.Ic 192.168.20.0/24
+.Ic # A start - end range
+.Ic 192.168.21.0 - 192.168.21.255
+.Ic # A masked address
+.Ic 192.168.22.0:255.255.255.0
+.Ic # As a single IP address
+.Ic 192.168.23.1
+.Ed
+.Sh BLACKLIST FILES
+additional files given as parameters to
+.Nm
+will be read to configure blacklists. The blacklist file format is
+as follows.
+.Bd -literal -offset indent
+.Ic SPAMD_SOURCE;mylist;"Sorry %A, You are a probably spammer\enBye\en"
+.Ic file:/usr/local/share/spammers.txt
+.Ic http://www.somewhere.org/bigspamblocks.txt
+.Ic SPAMD_SOURCE_REMOVE
+.Ic file:/usr/local/share/notspammers.txt
+.Ic file://www.somewhereelse.org/notspamblocks.txt
+.Ic SPAMD_SOURCE;mykorealist;"Your address %A, appears to be from korea"
+.Ic http://www.okean.com/koreacidr.txt
+.Ic SPAMD_SOURCE_REMOVE
+.Ic file:/usr/local/share/taekwondopals
+.Ed
+.Pp
+The
+.Li SPAMD_SOURCE
+line includes a tag to name the blacklist, and the message to be
+given to any connections that match this list. the message must
+be enclosed in double quotes
+and may include \en to produce a newline in the output. \e\" will produce
+a double quote in the output, and %% will produce a single % in the output.
+%A will be expanded by
+.Xr spamd 8
+to display the connecting IP address in the output.
+.Pp
+Following the
+.Li SPAMD_SOURCE
+should be URL's, one per line, from which to fetch the
+network blocks to blacklist.
+Following the
+.Li SPAMD_SOURCE_REMOVE
+line may be further URL's, one per line, from which to
+fetch network blocks that will be removed from this blacklist.
.Sh SEE ALSO
.Xr ftp 1 ,
.Xr pf 4 ,
diff --git a/libexec/spamd/spamd-setup.pl b/libexec/spamd/spamd-setup.pl
new file mode 100644
index 00000000000..cbf0696667c
--- /dev/null
+++ b/libexec/spamd/spamd-setup.pl
@@ -0,0 +1,310 @@
+#!/usr/bin/perl
+
+# $OpenBSD: spamd-setup.pl,v 1.1 2003/03/02 19:22:00 beck Exp $
+#
+# Copyright (c) 2003 Bob Beck <beck@openbsd.org>. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+use strict;
+use Net::Netmask;
+use Socket;
+
+my $i;
+my @blacklist;
+my $globalwhite;
+my %Sources;
+my $state=0;
+my $tag;
+my $file;
+
+sub quad2int
+{
+ my @bytes = split(/\./,$_[0]);
+
+ return undef unless @bytes == 4 && ! grep {!(/\d+$/ && $_<256)} @bytes;
+
+ return unpack("N",pack("C4",@bytes));
+}
+
+sub int2quad
+{
+ return join('.',unpack('C4', pack("N", $_[0])));
+}
+
+sub valid_addr() {
+ my @bytes = split(/\./,$_[0]);
+ return undef unless @bytes == 4 && ! grep {!(/\d+$/ && $_<256)} @bytes;
+ return($_[0]);
+}
+
+sub prev_addr() {
+ my @bytes = split(/\./,$_[0]);
+ return undef unless @bytes == 4 && ! grep {!(/\d+$/ && $_<256)} @bytes;
+ return int2quad (unpack("N",pack("C4",@bytes)) - 1);
+}
+
+sub next_addr() {
+ my @bytes = split(/\./,$_[0]);
+ return undef unless @bytes == 4 && ! grep {!(/\d+$/ && $_<256)} @bytes;
+ return int2quad (unpack("N",pack("C4",@bytes)) + 1);
+}
+
+# retrieve lists of netblocks or ip's from a list or urls as first
+# arg. Add valid addresses and blocks seen to seen hash (second arg),
+# as well as incrementing and decrementing start/end values in list hash
+# (third arg) for later use in computing where the list actually
+# starts and ends.
+sub retrieve_lists() { my ($urls, $seen, $list) = @_;
+ my $file = shift(@{$urls});
+ for (; $file && open (LIST, "ftp -V -o - $file |");
+ $file=shift(@{$urls})) {
+ while (<LIST>) {
+ my ($block, $start, $end);
+ # vanna vanna, find me a netblock we assume one per line
+ chomp;
+ $start = undef;
+ $end = undef;
+ next if (/^\s*\#/);
+ if ($_ =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2})/) {
+ #We're in CIDR format.......
+ $block = new Net::Netmask($1);
+ if (!$block->{'ERROR'}) {
+ $start = $block->base();
+ $end = $block->next();
+ }
+ } elsif ($_ =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})[\s-]+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,2})/){
+ #We're in somthing like startaddress - endaddress (Whois)
+ $start = &valid_addr($1);
+ $end = &next_addr($2);
+ } elsif ($_ =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) {
+ #We're in somthing like address:mask
+ $block = new Net::Netmask("$1:$2");
+ if (!$block->{'ERROR'}) {
+ $start = $block->base();
+ $end = $block->next();
+ }
+ }
+ elsif ($_ =~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) {
+ #We're just a single solitary IP.
+ $start = &valid_addr($1);
+ $end = &next_addr($1);
+ } else {
+ # skip anything that doesn't look like an IP or netblock.
+ next;
+ }
+ if ($start && $end) {
+ ${$seen}{"$end"}++;
+ ${$seen}{"$start"}++;
+ ${$list}{"$start"}++;
+ ${$list}{"$end"}--;
+ }
+ }
+ close(LIST);
+ }
+}
+
+# make a spamd blacklist - retrieve addressen/netblocks from sources
+# in first arg, remove errors or execptions from sources in second
+# arg, return a list of strings, consisting of the actual netblocks to
+# blacklist in CIDR format.
+sub blacklist () { my @args = @_;
+
+ my $i;
+ my (@blackurls, @errurls);
+ my (%Seen, %Black, %White, @BlackBlocks);
+ my ($blackval, $whiteval, $blackstart, $laststate);
+ my $j = 0;
+
+ # first, snarf the blacklist and extract the addresses.
+ for ($i = 0; $i<=$#args; $i++) {
+ if ($args[$i] =~ /^-w$/) {
+ $j = 1;
+ } else {
+ if ($j == 0) {
+ push (@blackurls, $args[$i]);
+ } else {
+ push (@errurls, $args[$i]);
+ }
+ }
+ }
+
+ &retrieve_lists(\@blackurls, \%Seen, \%Black);
+ &retrieve_lists(\@errurls, \%Seen, \%White);
+
+ foreach $a (sort_by_ip_address (keys %Seen)) {
+ my $newblack;
+ my $newwhite;
+ my $state;
+ $newblack = $Black{$a}?$Black{$a}:$blackval;
+ $newwhite = $White{$a}?$White{$a}:$whiteval;
+ if ($state == 0 && $newblack > 0) {
+ $state = 1;
+ }
+ elsif ($state == 1 && $newblack == 0) {
+ $state = 0;
+ }
+ if ($newwhite > 0) {
+ $state = 0;
+ }
+ if ($laststate == 0 && $state == 1) {
+ # start a blacklist
+ $blackstart = $a;
+ }
+ if ($laststate == 1 && $state == 0) {
+ # end a blacklist
+ push (@BlackBlocks, ( map {$_->desc()}
+ (range2cidrlist ($blackstart, &prev_addr($a)))));
+ }
+ $laststate = $state;
+ $blackval = $newblack;
+ $whiteval = $newwhite;
+ }
+ return @BlackBlocks;
+}
+
+
+# Tell spamd about a blacklist.
+# returns list of blacklisted CIDR's, suitable for adding to pf rdr.
+sub addblack () {
+ my ($name, $message, @urls) = @_;
+ my @blacknets = &blacklist(@urls);
+
+ if ($#blacknets >= 0) {
+ my ($i, $remote, $port, $iaddr, $paddr, $proto, $line);
+
+ # tell spamd about it
+ $remote = '127.0.0.1';
+ $port = 8025;
+ $iaddr = inet_aton($remote);
+ $paddr = sockaddr_in($port, $iaddr);
+ $proto = getprotobyname('tcp');
+ socket(SOCK, PF_INET, SOCK_STREAM, $proto) || die "socket $!";
+ connect(SOCK, $paddr) || die "connect: $!";
+ $line = "$name;$message;".join(";", @blacknets)."\n";
+ print SOCK $line;
+ close (SOCK);
+ }
+ return(@blacknets); # caller must add to pf table.
+}
+
+
+
+while ($_ = shift(@ARGV)) {
+ if ($_ !~ /^-/) {
+ unshift(@ARGV, $_);
+ last;
+ } else {
+ if ( /-s/ || /-1/ ) {
+ # spews level 1;
+ $Sources{"spews-1"}= [ "spews-1",
+ "\"SPAM. Your address %A is in spews level 1\\n".
+ "See http://www.spews.org/ask.cgi?x=%A for more details\\n\"",
+ "http://www.spews.org/spews_list_level1.txt", "-w" ];
+ } elsif (/-2/) {
+ # spews level 2
+ $Sources{"spews-2"}= [ "spews-2",
+ "\"SPAM. Your address %A is in spews level 2\\n".
+ "See http://www.spews.org/ask.cgi?x=%A for more details\\n\"",
+ "http://www.spews.org/spews_list_level2.txt", "-w" ];
+ } elsif (/-k/) {
+ # korea
+ $Sources{"korea-okean"}= [ "korea-okean",
+ "\"SPAM. Your address %A appears to be from korea\\n".
+ "See http://www.okean.com/asianspamblocks.html for more".
+ " details\\n\"",
+ "http://www.okean.com/koreacidr.txt", "-w" ];
+ } elsif (/-c/) {
+ # china
+ $Sources{"china-okean"}= [ "china-okean",
+ "\"SPAM. Your address %A appears to be from china\\n".
+ "See http://www.okean.com/asianspamblocks.html for more".
+ " details\\n\"",
+ "http://www.okean.com/chinacidr.txt", "-w" ];
+ } elsif (/-w/) {
+ # global whitelist file
+ $globalwhite = shift(@ARGV);
+ } elsif (/-f/) {
+ # local blacklist file
+ my $blackfile = shift(@ARGV);
+ if ($blackfile) {
+ $Sources{"localblock"}= [ "localblock",
+ "\"SPAM. Your address %A has been locally blocked\\n" .
+ "as a SPAM source\\n\"",
+ "file:$blackfile", "-w" ];
+ }
+ }
+ }
+}
+
+## read remaining input as files, setting up other lists.
+##
+## format must be
+#SPAMD_SOURCE:tag:"message to send to luser"
+#blacklist url
+#blacklist url
+#SPAMD_SOURCE_REMOVE
+#removelist url
+#removelist url
+while ($file = shift(@ARGV)) {
+ open(SETUP, "<$file") || die ("can't open $file");
+ while (<SETUP>) {
+ chomp;
+ next if (/^\s*\#/);
+ if (/^SPAMD_SOURCE;([^;]*);(\".*\")$/) {
+ if ($state == 1) {
+ # finish off last one.
+ push(@{$Sources{$tag}}, "-w");
+ }
+ $state = 1;
+ $tag = $1;
+ $Sources{$tag}=[ $tag, $2 ];
+ } elsif ($state > 0) {
+
+ if (/^SPAMD_SOURCE_REMOVE$/) {
+ push(@{$Sources{$tag}}, "-w");
+ $state = 2;
+ }
+ else {
+ push(@{$Sources{$tag}}, $_);
+ }
+ }
+ }
+}
+
+
+foreach $i (keys %Sources) {
+ my @args = @{$Sources{$i}};
+ if ($globalwhite) {
+ push(@args, "file:$globalwhite");
+ }
+ push (@blacklist, &addblack(@args));
+}
+
+
+# replace spamd table with new blacklist.
+open (PFCTL, "|pfctl -q -t spamd -T replace -f -") ||
+ die ("can't exec pfctl");
+for ($i=0; $i<=$#blacklist; $i++) {
+ print PFCTL $blacklist[$i], "\n";
+}
+close(PFCTL);
diff --git a/libexec/spamd/spamd.8 b/libexec/spamd/spamd.8
index 99fe382d0ce..9eea8d0e559 100644
--- a/libexec/spamd/spamd.8
+++ b/libexec/spamd/spamd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: spamd.8,v 1.11 2003/02/26 15:05:07 david Exp $
+.\" $OpenBSD: spamd.8,v 1.12 2003/03/02 19:22:00 beck Exp $
.\"
.\" Copyright (c) 2002 Theo de Raadt. All rights reserved.
.\"
@@ -103,31 +103,65 @@ The
rules used for this purpose are described in
.Xr pf.conf 5 .
The rules can be loaded into an
-.Em anchor
+.Em table
to simplify handling.
-If the main ruleset contains the following
-.Em rdr-anchor rule ,
-all
-.Em rdr
-rules inside the specified
-.Em anchor
-are evaluated for SMTP connections:
.Bd -literal
- rdr-anchor spews proto tcp from any to any port smtp
+ table <spamd>
+ rdr proto tcp from { <spamd> } to any port smtp -> 127.0.0.1 port 8025
.Ed
.Pp
-And all
-.Em rdr
-rules related to
+Any addresses in table
+.Aq spamd
+are then redirected to
.Nm
-can be loaded into one or more rulesets inside that
-.Em anchor ,
+running on port 25.
+Addresses can then be can be loaded into the
+.Em table ,
like:
.Bd -literal
- echo "rdr inet proto tcp from { 10.1.2.3, 10.2.3.4/30, 10.3.4.5/24 }
- to any port smtp -> 127.0.0.1 port 8025" | pfctl -a spews:first -f -
+ pfctl -q -t spamd -T replace -f /usr/local/share/spammers
.Ed
.Pp
+.Xr spamd-setup 8
+can also be used to load addresses into the
+.Aq spamd
+table.
+.Xr spamd-setup 8
+also has the added benefit of being able to remove addresses from
+blacklists, and will connect to
+.Nm
+over a localhost socket, giving
+.Nm
+information about each source of blacklist addresses, as well as custom
+rejection messages for each blacklist source
+that can be used to let any real person whose mail
+is deferred by spamd know why their address has been listed
+from sending mail. This is important as it allows legitimate mail
+senders to pressure spam sources into behaving properly so that they
+may be removed from the relevant blacklists.
+.Pp
+.Sh CONFIGURATION CONNECTIONS
+.Nm
+receives configuration information through a localhost connection
+Configuration information consists of blacklists, each with a name,
+a message to reject mail with, and addresses in CIDR format. When
+multiple lists are configured, Each configuration line specifies
+a blacklist, and must have the format:
+.Bd -literal
+ tag;"rejection message";aaa.bbb.ccc.ddd/mm;aaa.bbb.ccc.ddd/mm
+.Ed
+
+The rejection message must be inside double quotes. a \e" will
+produce a double quote in the output. \en will produce a newline. %A
+will expand to the connecting ip address in dotted quad format, %% may
+be used to produce a single % in the output, \e\e will produce a
+single \e.
+.Nm
+will reject mail by displaying all the messages from all blacklists in which
+a connecting address is matched.
+.Xr spamd-setup 8
+is normally used to configure this informaiton.
+.Pp
.Sh SEE ALSO
.Xr pf.conf 5 ,
.Xr pfctl 8 ,
diff --git a/libexec/spamd/spamd.c b/libexec/spamd/spamd.c
index fed6346ff8f..245ec334438 100644
--- a/libexec/spamd/spamd.c
+++ b/libexec/spamd/spamd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: spamd.c,v 1.10 2003/02/11 01:41:10 deraadt Exp $ */
+/* $OpenBSD: spamd.c,v 1.11 2003/03/02 19:22:00 beck Exp $ */
/*
* Copyright (c) 2002 Theo de Raadt. All rights reserved.
@@ -41,6 +41,7 @@
#include <stdlib.h>
#include <getopt.h>
#include <err.h>
+#include "sdl.h"
char hostname[MAXHOSTNAMELEN];
struct syslog_data sdata = SYSLOG_DATA_INIT;
@@ -48,6 +49,12 @@ char *reply = NULL;
char *nreply = "450";
char *spamd = "spamd IP-based SPAM blocker";
+extern struct sdlist *blacklists;
+
+int conffd = -1;
+char *cb;
+size_t cbs, cbu;
+
time_t t;
#define MAXCON 200
@@ -59,6 +66,9 @@ int debug;
struct con {
int fd;
int state;
+ int af;
+ struct sockaddr_in sin;
+ void *ia;
char addr[32];
char mail[64], rcpt[64];
@@ -75,7 +85,9 @@ struct con {
int il;
char rend[5]; /* any chars in here causes input termination */
- char obuf[8192];
+ int obufalloc;
+ char *obuf;
+ size_t osize;
char *op;
int ol;
} *con;
@@ -88,21 +100,334 @@ usage(void)
exit(1);
}
+char *
+grow_obuf(struct con *cp, int off)
+{
+ char * tmp;
+ if (!cp->obufalloc)
+ cp->obuf = NULL;
+ tmp = realloc(cp->obuf, cp->osize + 8192);
+ if (tmp != NULL) {
+ cp->osize += 8192;
+ cp->obuf = tmp;
+ cp->obufalloc = 1;
+ return(cp->obuf + off);
+ }
+ return(NULL);
+}
+
+
+int
+parse_configline(char *line)
+{
+ char *cp, prev, *name, *msg;
+ static char **av = NULL;
+ static size_t ac = 0;
+ size_t au = 0;
+ int mdone = 0;
+
+ if (debug)
+ printf("read config line %40s ...\n", line);
+
+ name = line;
+
+ for (cp = name; *cp != ';'; cp++);
+ *cp++ = '\0';
+ msg = cp;
+ if (*cp++ != '"')
+ goto parse_error;
+ prev = '\0';
+ for (;!mdone;cp++) {
+ switch (*cp) {
+ case '\\':
+ if (!prev)
+ prev = *cp;
+ else
+ prev = '\0';
+ break;
+ case '"':
+ if (prev != '\\') {
+ cp++;
+ if (*cp == ';') {
+ mdone = 1;
+ *cp = '\0';
+ } else
+ goto parse_error;
+ }
+ break;
+ case '\0':
+ goto parse_error;
+ default:
+ prev = '\0';
+ }
+
+ }
+
+ do {
+ if (ac == au) {
+ char **tmp;
+ tmp = realloc(av, (ac + 2048) * sizeof(char *));
+ if (tmp == NULL) {
+ return (-1);
+ }
+ av = tmp;
+ ac += 2048;
+ }
+ } while ((av[au++] = strsep(&cp, ";")) != NULL);
+ if (au < 2)
+ goto parse_error;
+ else
+ sdl_add(name, msg, av, au - 1);
+ return(0);
+
+ parse_error:
+ if (debug > 0)
+ printf("bogus config line - need 'tag;message;a/m;a/m;a/m...'\n");
+ return (-1);
+
+}
+
+
+void
+parse_configs(void) {
+ int i;
+ char *start, *end;
+
+ if (cbu == cbs) {
+ char * tmp;
+ tmp = realloc(cb, cbs + 8192);
+ if (tmp == NULL) {
+ if (debug > 0)
+ perror("malloc()");
+ free(cb);
+ cbs = cbu = 0;
+ return;
+ }
+ cbs += 8192;
+ cb = tmp;
+ }
+ cb[cbu++]='\0';
+
+ start = cb;
+ end = start;
+ for (i = 0; i < cbu; i++) {
+ if (*end == '\n') {
+ *end = '\0';
+ if (end > start + 1)
+ parse_configline(start);
+ start = ++end;
+ }
+ else
+ ++end;
+ }
+ if (end > start + 1)
+ parse_configline(start);
+}
+
+
+void
+do_config(void)
+{
+ int n;
+
+ if (debug > 0)
+ printf("got configuration connection\n");
+
+ if (cbu == cbs) {
+ char * tmp;
+ tmp = realloc(cb, cbs + 8192);
+ if (tmp == NULL) {
+ if (debug > 0)
+ perror("malloc()");
+ free(cb);
+ cbs = 0;
+ goto configdone;
+ }
+ cbs += 8192;
+ cb = tmp;
+ }
+
+ n = read(conffd, cb+cbu, cbs-cbu);
+ if (debug > 0)
+ printf("read %d config bytes\n", n);
+ if (n == 0) {
+ parse_configs();
+ goto configdone;
+ } else if (n == -1) {
+ if (debug > 0)
+ perror("read()");
+ goto configdone;
+ } else {
+ cbu += n;
+ }
+ return;
+ configdone:
+ cbu = 0;
+ close(conffd);
+ conffd = -1;
+}
+
+
+int
+append_error_string (struct con *cp, size_t off, char *fmt, int af, void *ia)
+{
+ char sav = '\0';
+ static int lastcont = 0;
+ char *c = cp->obuf + off;
+ char *s = fmt;
+ size_t len = cp->osize - off;
+ int i = 0;
+
+ if (off == 0) {
+ lastcont = 0;
+ }
+ if (lastcont != 0)
+ cp->obuf[lastcont] = '-';
+ i += snprintf(c, len, "%s ", nreply);
+ lastcont = off + i - 1;
+ if (*s == '"')
+ s++;
+ while (*s) {
+ /* make sure we at minimum, have room to add a
+ * format code (4 bytes), and a v6 address(39 bytes)
+ * and a byte saved in sav.
+ */
+ if (i >= len - 46) {
+ c = grow_obuf(cp, off);
+ if (c == NULL)
+ goto no_mem;
+ len = cp->osize - (off + i);
+ }
+
+ if (c[i-1] == '\n') {
+ if (lastcont != 0)
+ cp->obuf[lastcont] = '-';
+ i += snprintf(c + i, len, "%s ", nreply);
+ lastcont = off + i - 1;
+ }
+
+ switch (*s) {
+ case '\\':
+ case '%':
+ if (!sav)
+ sav = *s;
+ else {
+ c[i++] = sav;
+ sav = '\0';
+ c[i] = '\0';
+ }
+ break;
+ case '"':
+ case 'A':
+ case 'n':
+ if (*(s+1) == '\0') {
+ break;
+ }
+ if (sav == '\\' && *s == 'n') {
+ c[i++] = '\n';
+ sav = '\0';
+ c[i] = '\0';
+ break;
+ } else if (sav == '\\' && *s == '"') {
+ c[i++] = '"';
+ sav = '\0';
+ c[i] = '\0';
+ break;
+ } else if (sav == '%' && *s == 'A') {
+ inet_ntop(af, ia, c + i, (len - i));
+ i += strlen(c + i);
+ sav = '\0';
+ break;
+ }
+ /* fallthrough */
+ default:
+ if (sav)
+ c[i++] = sav;
+ c[i++] = *s;
+ sav='\0';
+ c[i] = '\0';
+ break;
+ }
+ s++;
+ }
+ return(i);
+ no_mem:
+ /* Out of memory, free obuf and bail, caller must deal */
+ if (cp->osize)
+ free(cp->obuf);
+ cp->osize = 0;
+ cp->obuf = NULL;
+ return(-1);
+}
+
+
+void
+build_reply(struct con * cp)
+{
+ struct sdlist **matches;
+ int off = 0;
+
+ matches = sdl_lookup(blacklists, cp->af, cp->ia);
+ if (matches == NULL) {
+ if (cp->osize)
+ free(cp->obuf);
+ cp->osize = 0;
+ cp->obuf = NULL;
+ goto bad;
+ }
+ for (; *matches; matches++) {
+ int used = 0;
+ char *c = cp->obuf + off;
+ int left = cp->osize - off;
+ used = append_error_string(cp, off, matches[0]->string,
+ cp->af, cp->ia);
+ if (used == -1)
+ goto bad;
+ off += used;
+ left -= used;
+ if (cp->obuf[off - 1] != '\n') {
+ if ( left < 1) {
+ c = grow_obuf(cp, off);
+ if (c == NULL) {
+ if (cp->osize)
+ free(cp->obuf);
+ cp->osize = 0;
+ cp->obuf = NULL;
+ goto bad;
+ }
+ }
+ cp->obuf[off++]='\n';
+ cp->obuf[off]='\0';
+ }
+ }
+ return;
+bad:
+ /* Out of memory, or no match. give generic reply */
+ asprintf(&cp->obuf,
+ "%s-Sorry %s\n"
+ "%s-You are trying to send mail from an address listed by one\n"
+ "%s or more IP-based registries as being a SPAM source.\n",
+ nreply, cp->addr, nreply, nreply);
+ if (cp->obuf == NULL) {
+ /* we're having a really bad day.. */
+ cp->obufalloc = 0; /* know not to free or mangle */
+ cp->obuf="450 Try again\r\n";
+ } else {
+ cp->osize = strlen(cp->obuf) + 1;
+ }
+}
+
void
doreply(struct con *cp)
{
if (reply) {
- snprintf(cp->obuf, sizeof cp->obuf,
+ if (!cp->obufalloc)
+ err(1, "shouldn't happen");
+ snprintf(cp->obuf, cp->osize,
"%s %s\n", nreply, reply);
return;
}
-
- snprintf(cp->obuf, sizeof cp->obuf,
- "%s-SPAM. www.spews.org/ask.cgi?x=%s\n"
- "%s-You are trying to send mail from an address listed by one or\n"
- "%s-more IP-based registries as being in a SPAM-generating netblock.\n"
- "%s SPAM. www.spews.org/ask.cgi?x=%s\n",
- nreply, cp->addr, nreply, nreply, nreply, cp->addr);
+ build_reply(cp);
}
void
@@ -131,10 +456,18 @@ initcon(struct con *cp, int fd, struct sockaddr_in *sin)
time_t t;
time(&t);
+ if (cp->obufalloc) {
+ free(cp->obuf);
+ }
bzero(cp, sizeof(struct con));
+ if (grow_obuf(cp, 0) == NULL)
+ err(1, "malloc");
cp->fd = fd;
+ memcpy(&cp->sin, sin, sizeof(struct sockaddr_in));
+ cp->af = sin->sin_family;
+ cp->ia = (void *) &cp->sin.sin_addr;
strlcpy(cp->addr, inet_ntoa(sin->sin_addr), sizeof(cp->addr));
- snprintf(cp->obuf, sizeof(cp->obuf),
+ snprintf(cp->obuf, cp->osize,
"220 %s ESMTP %s; %s",
hostname, spamd, ctime(&t));
cp->op = cp->obuf;
@@ -154,6 +487,11 @@ closecon(struct con *cp)
time(&t);
printf("%s connected for %d seconds.\n", cp->addr, t - cp->s);
}
+ if (cp->osize > 0 && cp->obufalloc) {
+ free(cp->obuf);
+ cp->obuf = NULL;
+ cp->osize = 0;
+ }
close(cp->fd);
clients--;
cp->fd = -1;
@@ -180,7 +518,7 @@ nextstate(struct con *cp)
/* received input: parse, and select next state */
if (match(cp->ibuf, "HELO") ||
match(cp->ibuf, "EHLO")) {
- snprintf(cp->obuf, sizeof cp->obuf,
+ snprintf(cp->obuf, cp->osize,
"250 Hello, spam sender. "
"Pleased to be wasting your time.\n");
cp->op = cp->obuf;
@@ -201,9 +539,9 @@ nextstate(struct con *cp)
case 3:
if (match(cp->ibuf, "MAIL")) {
setlog(cp->mail, sizeof cp->mail, cp->ibuf);
- snprintf(cp->obuf, sizeof cp->obuf,
+ snprintf(cp->obuf, cp->osize,
"250 You are about to try to deliver spam. "
- "Your time will be spent, amounting to nothing.\n");
+ "Your time will be spent, for nothing.\n");
cp->op = cp->obuf;
cp->ol = strlen(cp->op);
cp->state = 4;
@@ -222,7 +560,7 @@ nextstate(struct con *cp)
case 5:
if (match(cp->ibuf, "RCPT")) {
setlog(cp->rcpt, sizeof(cp->rcpt), cp->ibuf);
- snprintf(cp->obuf, sizeof cp->obuf,
+ snprintf(cp->obuf, cp->osize,
"250 This is hurting you more than it is "
"hurting me.\n");
cp->op = cp->obuf;
@@ -295,6 +633,19 @@ handlew(struct con *cp, int one)
int n;
if (cp->w) {
+ if (*cp->op == '\n') {
+ /* insert \r before \n */
+ n = write(cp->fd, "\r", 1);
+ if (n == 0) {
+ closecon(cp);
+ goto handled;
+ } else if (n == -1) {
+ if (debug > 0 && errno != EPIPE)
+ perror("write()");
+ closecon(cp);
+ goto handled;
+ }
+ }
n = write(cp->fd, cp->op, one ? 1 : cp->ol);
if (n == 0) {
closecon(cp);
@@ -307,6 +658,7 @@ handlew(struct con *cp, int one)
cp->ol -= n;
}
}
+ handled:
cp->w = t + 1;
if (cp->ol == 0) {
cp->w = 0;
@@ -319,8 +671,9 @@ main(int argc, char *argv[])
{
fd_set *fdsr = NULL, *fdsw = NULL;
struct sockaddr_in sin;
+ struct sockaddr_in lin;
struct passwd *pw;
- int ch, s, s2, i, omax = 0;
+ int ch, s, s2, conflisten = 0, i, omax = 0;
int sinlen, one = 1;
u_short port = 8025;
@@ -367,6 +720,12 @@ main(int argc, char *argv[])
if (con == NULL)
err(1, "calloc");
+ con->obuf = malloc(8192);
+
+ if (con->obuf == NULL)
+ err(1, "malloc");
+ con->osize = 8192;
+
for (i = 0; i < maxcon; i++)
con[i].fd = -1;
@@ -380,14 +739,32 @@ main(int argc, char *argv[])
sizeof(one)) == -1)
return(-1);
+ conflisten = socket(AF_INET, SOCK_STREAM, 0);
+ if (conflisten == -1)
+ err(1, "socket");
+
+ if (setsockopt(conflisten, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(one)) == -1)
+ return(-1);
+
memset(&sin, 0, sizeof sin);
sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if (bind(s, (struct sockaddr *)&sin, sizeof sin) == -1)
err(1, "bind");
+ memset(&lin, 0, sizeof sin);
+ lin.sin_len = sizeof(sin);
+ lin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ lin.sin_family = AF_INET;
+ lin.sin_port = htons(port);
+
+ if (bind(conflisten, (struct sockaddr *)&lin, sizeof lin) == -1)
+ err(1, "bind local");
+
pw = getpwnam("_spamd");
if (!pw)
pw = getpwnam("nobody");
@@ -408,6 +785,9 @@ main(int argc, char *argv[])
if (listen(s, 10) == -1)
err(1, "listen");
+ if (listen(conflisten, 10) == -1)
+ err(1, "listen");
+
if (debug == 0) {
if (daemon(1, 1) == -1)
err(1, "fork");
@@ -416,8 +796,10 @@ main(int argc, char *argv[])
while (1) {
struct timeval tv, *tvp;
- int max = s, i, n;
+ int max, i, n;
int writers;
+ max = MAX(s, conflisten);
+ max = MAX(max, conffd);
time(&t);
for (i = 0; i < maxcon; i++)
@@ -466,6 +848,13 @@ main(int argc, char *argv[])
}
FD_SET(s, fdsr);
+ /* only one active config conn at a time */
+ if (conffd == -1)
+ FD_SET(conflisten, fdsr);
+ else
+ FD_SET(conffd, fdsr);
+
+
if (writers == 0) {
tvp = NULL;
} else {
@@ -502,6 +891,20 @@ main(int argc, char *argv[])
else
initcon(&con[i], s2, &sin);
}
+ if (FD_ISSET(conflisten, fdsr)) {
+ sinlen = sizeof(lin);
+ conffd = accept(conflisten, (struct sockaddr *)&lin,
+ &sinlen);
+ if (conffd == -1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "accept");
+ }
+ }
+ if (conffd != -1 && FD_ISSET(conffd, fdsr)) {
+ do_config();
+ }
+
}
exit(1);
}