aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKurt Lidl <lidl@FreeBSD.org>2016-06-01 21:48:22 +0000
committerKurt Lidl <lidl@FreeBSD.org>2016-06-01 21:48:22 +0000
commitdf0cfa3b86180bc007aafedfac6685826e8de7ea (patch)
tree87b2b18820b6cc1642b930ba61c9228f4f3bb48b
downloadsrc-df0cfa3b86180bc007aafedfac6685826e8de7ea.tar.gz
src-df0cfa3b86180bc007aafedfac6685826e8de7ea.zip
Approved by: rpaulo Obtained from: NetBSD external/bsd/blacklist @ 20160409 Relnotes: YES Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D5912
Notes
Notes: svn path=/vendor/NetBSD/blacklist/dist/; revision=301169 svn path=/vendor/NetBSD/blacklist/20160409/; revision=301170; tag=vendor/NetBSD/blacklist/20160409
-rw-r--r--Makefile5
-rw-r--r--Makefile.inc10
-rw-r--r--README103
-rw-r--r--TODO21
-rw-r--r--bin/Makefile15
-rw-r--r--bin/blacklistctl.881
-rw-r--r--bin/blacklistctl.c151
-rw-r--r--bin/blacklistd.8222
-rw-r--r--bin/blacklistd.c537
-rw-r--r--bin/blacklistd.conf.5222
-rw-r--r--bin/conf.c1142
-rw-r--r--bin/conf.h65
-rw-r--r--bin/internal.c48
-rw-r--r--bin/internal.h57
-rw-r--r--bin/run.c156
-rw-r--r--bin/run.h41
-rw-r--r--bin/state.c233
-rw-r--r--bin/state.h62
-rw-r--r--bin/support.c157
-rw-r--r--bin/support.h44
-rw-r--r--diff/ftpd.diff91
-rw-r--r--diff/named.diff216
-rw-r--r--diff/proftpd.diff124
-rw-r--r--diff/ssh.diff231
-rw-r--r--etc/Makefile10
-rw-r--r--etc/blacklistd.conf14
-rw-r--r--etc/npf.conf15
-rw-r--r--etc/rc.d/Makefile6
-rw-r--r--etc/rc.d/blacklistd57
-rw-r--r--include/Makefile10
-rw-r--r--include/bl.h76
-rw-r--r--include/blacklist.h46
-rw-r--r--lib/Makefile19
-rw-r--r--lib/bl.c524
-rw-r--r--lib/blacklist.c88
-rw-r--r--lib/libblacklist.3125
-rw-r--r--lib/shlib_version2
-rw-r--r--libexec/Makefile6
-rw-r--r--libexec/blacklistd-helper82
-rw-r--r--port/Makefile.am25
-rw-r--r--port/_strtoi.h93
-rw-r--r--port/clock_gettime.c17
-rw-r--r--port/config.h3
-rw-r--r--port/configure.ac91
-rw-r--r--port/fgetln.c106
-rw-r--r--port/fparseln.c236
-rw-r--r--port/getprogname.c24
-rw-r--r--port/m4/.cvsignore1
-rw-r--r--port/pidfile.c183
-rw-r--r--port/popenve.c274
-rw-r--r--port/port.h86
-rw-r--r--port/sockaddr_snprintf.c383
-rw-r--r--port/strlcat.c96
-rw-r--r--port/strlcpy.c78
-rw-r--r--port/strtoi.c61
-rw-r--r--test/Makefile12
-rw-r--r--test/cltest.c136
-rw-r--r--test/srvtest.c220
58 files changed, 7239 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000000..da4411d0ca75
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.2 2015/01/22 17:49:41 christos Exp $
+
+SUBDIR = lib .WAIT include bin etc libexec
+
+.include <bsd.subdir.mk>
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 000000000000..85c82783cd35
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile.inc,v 1.3 2015/01/23 03:57:22 christos Exp $
+
+WARNS=6
+.if !defined(LIB)
+LDADD+= -lblacklist
+DPADD+= ${LIBBLACKLIST}
+.endif
+CPPFLAGS+= -I${.CURDIR}/../include
+CPPFLAGS+=-DHAVE_STRUCT_SOCKADDR_SA_LEN -DHAVE_UTIL_H -DHAVE_DB_H
+
diff --git a/README b/README
new file mode 100644
index 000000000000..44e1026ac4db
--- /dev/null
+++ b/README
@@ -0,0 +1,103 @@
+# $NetBSD: README,v 1.7 2015/01/26 00:34:50 christos Exp $
+
+This package contains library that can be used by network daemons to
+communicate with a packet filter via a daemon to enforce opening and
+closing ports dynamically based on policy.
+
+The interface to the packet filter is in libexec/blacklistd-helper
+(this is currently designed for npf) and the configuration file
+(inspired from inetd.conf) is in etc/blacklistd.conf.
+
+On NetBSD you can find an example npf.conf and blacklistd.conf in
+/usr/share/examples/blacklistd; you need to adjust the interface
+in npf.conf and copy both files to /etc; then you just enable
+blacklistd=YES in /etc/rc.conf, start it up, and you are all set.
+
+There is also a startup file in etc/rc.d/blacklistd
+
+Patches to various daemons to add blacklisting capabilitiers are in the
+"diff" directory:
+ - OpenSSH: diff/ssh.diff [tcp socket example]
+ - Bind: diff/named.diff [both tcp and udp]
+ - ftpd: diff/ftpd.diff [tcp]
+
+These patches have been applied to NetBSD-current.
+
+The network daemon (for example sshd) communicates to blacklistd, via
+a unix socket like syslog. The library calls are simple and everything
+is handled by the library. In the simplest form the only thing the
+daemon needs to do is to call:
+
+ blacklist(action, acceptedfd, message);
+
+Where:
+ action = 0 -> successful login clear blacklist state
+ 1 -> failed login, add to the failed count
+ acceptedfd -> the file descriptor where the server is
+ connected to the remote client. It is used
+ to determine the listening socket, and the
+ remote address. This allows any program to
+ contact the blacklist daemon, since the verification
+ if the program has access to the listening
+ socket is done by virtue that the port
+ number is retrieved from the kernel.
+ message -> an optional string that is used in debugging logs.
+
+Unfortunately there is no way to get information about the "peer"
+from a udp socket, because there is no connection and that information
+is kept with the server. In that case the daemon can provide the
+peer information to blacklistd via:
+
+ blacklist_sa(action, acceptedfd, sockaddr, sockaddr_len, message);
+
+The configuration file contains entries of the form:
+
+# Blacklist rule
+# host/Port type protocol owner name nfail disable
+192.168.1.1:ssh stream tcp * -int 10 1m
+8.8.8.8:ssh stream tcp * -ext 6 60m
+ssh stream tcp6 * * 6 60m
+http stream tcp * * 6 60m
+
+Here note that owner is * because the connection is done from the
+child ssh socket which runs with user privs. We treat ipv4 connections
+differently by maintaining two different rules one for the external
+interface and one from the internal We also register for both tcp
+and tcp6 since those are different listening sockets and addresses;
+we don't bother with ipv6 and separate rules. We use nfail = 6,
+because ssh allows 3 password attempts per connection, and this
+will let us have 2 connections before blocking. Finally we block
+for an hour; we could block forever too by specifying * in the
+duration column.
+
+blacklistd and the library use syslog(3) to report errors. The
+blacklist filter state is persisted automatically in /var/db/blacklistd.db
+so that if the daemon is restarted, it remembers what connections
+is currently handling. To start from a fresh state (if you restart
+npf too for example), you can use -f. To watch the daemon at work,
+you can use -d.
+
+The current control file is designed for npf, and it uses the
+dynamic rule feature. You need to create a dynamic rule in your
+/etc/npf.conf on the group referring to the interface you want to block
+called blacklistd as follows:
+
+ext_if=bge0
+int_if=sk0
+
+group "external" on $ext_if {
+ ...
+ ruleset "blacklistd-ext"
+ ruleset "blacklistd"
+ ...
+}
+
+group "internal" on $int_if {
+ ...
+ ruleset "blacklistd-int"
+ ...
+}
+
+Enjoy,
+
+christos
diff --git a/TODO b/TODO
new file mode 100644
index 000000000000..9925020d54bb
--- /dev/null
+++ b/TODO
@@ -0,0 +1,21 @@
+# $NetBSD: TODO,v 1.7 2015/01/23 21:34:01 christos Exp $
+
+- don't poll periodically, find the next timeout
+- use the socket also for commands? Or separate socket?
+- add functionality to the control program. Should it change the database
+ directly, or talk to the daemon to have it do it?
+- perhaps handle interfaces too instead of addresses for dynamic ip?
+ <bge0/4>? What to do with multiple addresses?
+- perhaps rate limit against DoS
+- perhaps instead of scanning the list have a sparse map by port?
+- do we want to use libnpf directly for efficiency?
+- add more daemons ftpd?
+- do we care about the db state becoming too large?
+- instead of a yes = bump one, no = return to 0 interface, do we want
+ to have something more flexible like?
+ +n
+ -n
+ block
+ unblock
+- do we need an api in blacklistctl to perform maintenance
+- fix the blacklistctl output to be more user friendly
diff --git a/bin/Makefile b/bin/Makefile
new file mode 100644
index 000000000000..280c72fd3af1
--- /dev/null
+++ b/bin/Makefile
@@ -0,0 +1,15 @@
+# $NetBSD: Makefile,v 1.11 2015/01/27 19:40:36 christos Exp $
+
+BINDIR=/sbin
+
+PROGS=blacklistd blacklistctl
+MAN.blacklistd=blacklistd.8 blacklistd.conf.5
+MAN.blacklistctl=blacklistctl.8
+SRCS.blacklistd = blacklistd.c conf.c run.c state.c support.c internal.c
+SRCS.blacklistctl = blacklistctl.c conf.c state.c support.c internal.c
+DBG=-g
+
+LDADD+=-lutil
+DPADD+=${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/bin/blacklistctl.8 b/bin/blacklistctl.8
new file mode 100644
index 000000000000..d8ed1f09cde2
--- /dev/null
+++ b/bin/blacklistctl.8
@@ -0,0 +1,81 @@
+.\" $NetBSD: blacklistctl.8,v 1.7 2015/04/30 06:20:43 riz Exp $
+.\"
+.\" Copyright (c) 2015 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Christos Zoulas.
+.\"
+.\" 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``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 FOUNDATION OR CONTRIBUTORS
+.\" 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.
+.\"
+.Dd April 29, 2015
+.Dt BLACKLISTCTL 8
+.Os
+.Sh NAME
+.Nm blacklistctl
+.Nd display and change the state of blacklistd
+.Sh SYNOPSIS
+.Nm
+.Cm dump
+.Op Fl abdnrw
+.Sh DESCRIPTION
+.Nm
+is a program used to display the state of
+.Xr blacklistd 8
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Show all database entries, by default it shows only the embryonic ones.
+.It Fl b
+Show only the blocked entries.
+.It Fl d
+Increase debugging level.
+.It Fl n
+Don't display a header.
+.It Fl r
+Show the remaining blocked time instead of the last activity time.
+.It Fl w
+Normally the width of addresses is good for IPv4, the
+.Fl w
+flag, makes the display wide enough for IPv6 addresses.
+.El
+.Sh SEE ALSO
+.Xr blacklistd 8
+.Sh NOTES
+Sometimes the reported number of failed attempts can exceed the number
+of attempts that
+.Xr blacklistd 8
+is configured to block.
+This can happen either because the rule has been removed manually, or
+because there were more attempts in flight while the rule block was being
+added.
+This condition is normal; in that case
+.Xr blacklistd 8
+will first attempt to remove the existing rule, and then it will re-add
+it to make sure that there is only one rule active.
+.Sh HISTORY
+.Nm
+appeared in
+.Nx 7 .
+.Sh AUTHORS
+.An Christos Zoulas
diff --git a/bin/blacklistctl.c b/bin/blacklistctl.c
new file mode 100644
index 000000000000..d202fb159b89
--- /dev/null
+++ b/bin/blacklistctl.c
@@ -0,0 +1,151 @@
+/* $NetBSD: blacklistctl.c,v 1.20 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: blacklistctl.c,v 1.20 2016/04/04 15:52:56 christos Exp $");
+
+#include <stdio.h>
+#include <time.h>
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include "conf.h"
+#include "state.h"
+#include "internal.h"
+#include "support.h"
+
+static __dead void
+usage(int c)
+{
+ if (c == 0)
+ warnx("Missing/unknown command");
+ else
+ warnx("Unknown option `%c'", (char)c);
+ fprintf(stderr, "Usage: %s dump [-abdnrw]\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *dbname = _PATH_BLSTATE;
+ DB *db;
+ struct conf c;
+ struct dbinfo dbi;
+ unsigned int i;
+ struct timespec ts;
+ int all, blocked, remain, wide, noheader;
+ int o;
+
+ noheader = wide = blocked = all = remain = 0;
+ lfun = dlog;
+
+ if (argc == 1 || strcmp(argv[1], "dump") != 0)
+ usage(0);
+
+ argc--;
+ argv++;
+
+ while ((o = getopt(argc, argv, "abD:dnrw")) != -1)
+ switch (o) {
+ case 'a':
+ all = 1;
+ blocked = 0;
+ break;
+ case 'b':
+ blocked = 1;
+ case 'D':
+ dbname = optarg;
+ break;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'n':
+ noheader = 1;
+ break;
+ case 'r':
+ remain = 1;
+ break;
+ case 'w':
+ wide = 1;
+ break;
+ default:
+ usage(o);
+ break;
+ }
+
+ db = state_open(dbname, O_RDONLY, 0);
+ if (db == NULL)
+ err(EXIT_FAILURE, "Can't open `%s'", dbname);
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ wide = wide ? 8 * 4 + 7 : 4 * 3 + 3;
+ if (!noheader)
+ printf("%*.*s/ma:port\tid\tnfail\t%s\n", wide, wide,
+ "address", remain ? "remaining time" : "last access");
+ for (i = 1; state_iterate(db, &c, &dbi, i) != 0; i = 0) {
+ char buf[BUFSIZ];
+ if (!all) {
+ if (blocked) {
+ if (dbi.count < c.c_nfail)
+ continue;
+ } else {
+ if (dbi.count >= c.c_nfail)
+ continue;
+ }
+ }
+ sockaddr_snprintf(buf, sizeof(buf), "%a", (void *)&c.c_ss);
+ printf("%*.*s/%d:%d\t", wide, wide, buf, c.c_lmask, c.c_port);
+ if (remain)
+ fmtydhms(buf, sizeof(buf),
+ c.c_duration - (ts.tv_sec - dbi.last));
+ else
+ fmttime(buf, sizeof(buf), dbi.last);
+ printf("%s\t%d/%d\t%-s\n", dbi.id, dbi.count, c.c_nfail, buf);
+ }
+ state_close(db);
+ return EXIT_SUCCESS;
+}
diff --git a/bin/blacklistd.8 b/bin/blacklistd.8
new file mode 100644
index 000000000000..ae684770357f
--- /dev/null
+++ b/bin/blacklistd.8
@@ -0,0 +1,222 @@
+.\" $NetBSD: blacklistd.8,v 1.15 2016/03/11 17:16:40 christos Exp $
+.\"
+.\" Copyright (c) 2015 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Christos Zoulas.
+.\"
+.\" 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``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 FOUNDATION OR CONTRIBUTORS
+.\" 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.
+.\"
+.Dd June 4, 2015
+.Dt BLACKLISTD 8
+.Os
+.Sh NAME
+.Nm blacklistd
+.Nd block and release ports on demand to avoid DoS abuse
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfrv
+.Op Fl C Ar controlprog
+.Op Fl c Ar configfile
+.Op Fl D Ar dbfile
+.Op Fl P Ar sockpathsfile
+.Op Fl R Ar rulename
+.Op Fl s Ar sockpath
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+.Nm
+is a daemon similar to
+.Xr syslogd 8
+that listens to a sockets at paths specified in the
+.Ar sockpathsfile
+for notifications from other daemons about successful or failed connection
+attempts.
+If no such file is specified, then it only listens to the socket path
+specified by
+.Ar sockspath
+or if that is not specified to
+.Pa /var/run/blacklistd.sock .
+Each notification contains an (action, port, protocol, address, owner) tuple
+that identifies the remote connection and the action.
+This tuple is consulted against entries in
+.Ar configfile
+with syntax specified in
+.Xr blacklistd.conf 5 .
+If an entry is matched, a state entry is created for that tuple.
+Each entry contains a number of tries limit and a duration.
+.Pp
+If the action is
+.Dq add
+and the number of tries limit is reached, then a
+control script
+.Ar controlprog
+is invoked with arguments:
+.Bd -literal -offset indent
+control add <rulename> <proto> <address> <mask> <port>
+.Ed
+.Pp
+and should invoke a packet filter command to block the connection
+specified by the arguments.
+The
+.Ar rulename
+argument can be set from the command line (default
+.Dv blacklistd ) .
+The script could print a numerical id to stdout as a handle for
+the rule that can be used later to remove that connection, but
+that is not required as all information to remove the rule is
+kept.
+.Pp
+If the action is
+.Dq remove
+Then the same control script is invoked as:
+.Bd -literal -offset indent
+control remove <rulename> <proto> <address> <mask> <port> <id>
+.Ed
+.Pp
+where
+.Ar id
+is the number returned from the
+.Dq add
+action.
+.Pp
+.Nm
+maintains a database of known connections in
+.Ar dbfile .
+On startup it reads entries from that file, and updates its internal state.
+.Pp
+.Nm
+checks the list of active entries every
+.Ar timeout
+seconds (default
+.Dv 15 )
+and removes entries and block rules using the control program as necessary.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl C Ar controlprog
+Use
+.Ar controlprog
+to communicate with the packet filter, usually
+.Pa /libexec/blacklistd-helper .
+The following arguments are passed to the control program:
+.Bl -tag -width protocol
+.It action
+The action to perform:
+.Dv add ,
+.Dv rem ,
+or
+.Dv flush
+to add, remove or flush a firewall rule.
+.It name
+The rule name.
+.It protocol
+The optional protocol name (can be empty):
+.Dv tcp ,
+.Dv tcp6 ,
+.Dv udp ,
+.Dv udp6 .
+.It address
+The IPv4 or IPv6 numeric address to be blocked or released.
+.It mask
+The numeric mask to be applied to the blocked or released address
+.It port
+The optional numeric port to be blocked (can be empty).
+.It id
+For packet filters that support removal of rules by rule identifier, the
+identifier of the rule to be removed.
+The add command is expected to return the rule identifier string to stdout.
+.El
+.It Fl c Ar configuration
+The name of the configuration file to read, usually
+.Pa /etc/blacklistd.conf .
+.It Fl D Ar dbfile
+The Berkeley DB file where
+.Nm
+stores its state, usually
+.Pa /var/run/blacklistd.db .
+.It Fl d
+Normally,
+.Nm
+disassociates itself from the terminal unless the
+.Fl d
+flag is specified, in which case it stays in the foreground.
+.It Fl f
+Truncate the state database and flush all the rules named
+.Ar rulename
+are deleted by invoking the control script as:
+.Bd -literal -offset indent
+control flush <rulename>
+.Ed
+.It Fl P Ar sockspathsfile
+A file containing a list of pathnames, one per line that
+.Nm
+will create sockets to listen to.
+This is useful for chrooted environments.
+.It Fl R Ar rulename
+Specify the default rule name for the packet filter rules, usually
+.Dv blacklistd .
+.It Fl r
+Re-read the firewall rules from the internal database, then
+remove and re-add them.
+This helps for packet filters that don't retain state across reboots.
+.It Fl s Ar sockpath
+Add
+.Ar sockpath
+to the list of Unix sockets
+.Nm
+listens to.
+.It Fl t Ar timeout
+The interval in seconds
+.Nm
+polls the state file to update the rules.
+.It Fl v
+Cause
+.Nm
+to print
+diagnostic messages to
+.Dv stdout
+instead of
+.Xr syslogd 8 .
+.El
+.Sh FILES
+.Bl -tag -width /libexec/blacklistd-helper -compact
+.It Pa /libexec/blacklistd-helper
+Shell script invoked to interface with the packet filter.
+.It Pa /etc/blacklistd.conf
+Configuration file.
+.It Pa /var/db/blacklistd.db
+Database of current connection entries.
+.It Pa /var/run/blacklistd.sock
+Socket to receive connection notifications.
+.El
+.Sh SEE ALSO
+.Xr blacklistd.conf 5 ,
+.Xr blacklistctl 8 ,
+.Xr npfctl 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+.Nm
+appeared in
+.Nx 7 .
+.Sh AUTHORS
+.An Christos Zoulas
diff --git a/bin/blacklistd.c b/bin/blacklistd.c
new file mode 100644
index 000000000000..c16b18e193a5
--- /dev/null
+++ b/bin/blacklistd.c
@@ -0,0 +1,537 @@
+/* $NetBSD: blacklistd.c,v 1.34 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: blacklistd.c,v 1.34 2016/04/04 15:52:56 christos Exp $");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#include <string.h>
+#include <signal.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+
+#include "bl.h"
+#include "internal.h"
+#include "conf.h"
+#include "run.h"
+#include "state.h"
+#include "support.h"
+
+static const char *configfile = _PATH_BLCONF;
+static DB *state;
+static const char *dbfile = _PATH_BLSTATE;
+static sig_atomic_t readconf;
+static sig_atomic_t done;
+static int vflag;
+
+static void
+sigusr1(int n __unused)
+{
+ debug++;
+}
+
+static void
+sigusr2(int n __unused)
+{
+ debug--;
+}
+
+static void
+sighup(int n __unused)
+{
+ readconf++;
+}
+
+static void
+sigdone(int n __unused)
+{
+ done++;
+}
+
+static __dead void
+usage(int c)
+{
+ if (c)
+ warnx("Unknown option `%c'", (char)c);
+ fprintf(stderr, "Usage: %s [-vdfr] [-c <config>] [-R <rulename>] "
+ "[-P <sockpathsfile>] [-C <controlprog>] [-D <dbfile>] "
+ "[-s <sockpath>] [-t <timeout>]\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+static int
+getremoteaddress(bl_info_t *bi, struct sockaddr_storage *rss, socklen_t *rsl)
+{
+ *rsl = sizeof(*rss);
+ memset(rss, 0, *rsl);
+
+ if (getpeername(bi->bi_fd, (void *)rss, rsl) != -1)
+ return 0;
+
+ if (errno != ENOTCONN) {
+ (*lfun)(LOG_ERR, "getpeername failed (%m)");
+ return -1;
+ }
+
+ if (bi->bi_slen == 0) {
+ (*lfun)(LOG_ERR, "unconnected socket with no peer in message");
+ return -1;
+ }
+
+ switch (bi->bi_ss.ss_family) {
+ case AF_INET:
+ *rsl = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ *rsl = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ (*lfun)(LOG_ERR, "bad client passed socket family %u",
+ (unsigned)bi->bi_ss.ss_family);
+ return -1;
+ }
+
+ if (*rsl != bi->bi_slen) {
+ (*lfun)(LOG_ERR, "bad client passed socket length %u != %u",
+ (unsigned)*rsl, (unsigned)bi->bi_slen);
+ return -1;
+ }
+
+ memcpy(rss, &bi->bi_ss, *rsl);
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ if (*rsl != rss->ss_len) {
+ (*lfun)(LOG_ERR,
+ "bad client passed socket internal length %u != %u",
+ (unsigned)*rsl, (unsigned)rss->ss_len);
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+static void
+process(bl_t bl)
+{
+ struct sockaddr_storage rss;
+ socklen_t rsl;
+ char rbuf[BUFSIZ];
+ bl_info_t *bi;
+ struct conf c;
+ struct dbinfo dbi;
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
+ (*lfun)(LOG_ERR, "clock_gettime failed (%m)");
+ return;
+ }
+
+ if ((bi = bl_recv(bl)) == NULL) {
+ (*lfun)(LOG_ERR, "no message (%m)");
+ return;
+ }
+
+ if (getremoteaddress(bi, &rss, &rsl) == -1)
+ goto out;
+
+ if (debug) {
+ sockaddr_snprintf(rbuf, sizeof(rbuf), "%a:%p", (void *)&rss);
+ (*lfun)(LOG_DEBUG, "processing type=%d fd=%d remote=%s msg=%s"
+ " uid=%lu gid=%lu", bi->bi_type, bi->bi_fd, rbuf,
+ bi->bi_msg, (unsigned long)bi->bi_uid,
+ (unsigned long)bi->bi_gid);
+ }
+
+ if (conf_find(bi->bi_fd, bi->bi_uid, &rss, &c) == NULL) {
+ (*lfun)(LOG_DEBUG, "no rule matched");
+ goto out;
+ }
+
+
+ if (state_get(state, &c, &dbi) == -1)
+ goto out;
+
+ if (debug) {
+ char b1[128], b2[128];
+ (*lfun)(LOG_DEBUG, "%s: db state info for %s: count=%d/%d "
+ "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
+ fmttime(b1, sizeof(b1), dbi.last),
+ fmttime(b2, sizeof(b2), ts.tv_sec));
+ }
+
+ switch (bi->bi_type) {
+ case BL_ADD:
+ dbi.count++;
+ dbi.last = ts.tv_sec;
+ if (dbi.id[0]) {
+ /*
+ * We should not be getting this since the rule
+ * should have blocked the address. A possible
+ * explanation is that someone removed that rule,
+ * and another would be that we got another attempt
+ * before we added the rule. In anycase, we remove
+ * and re-add the rule because we don't want to add
+ * it twice, because then we'd lose track of it.
+ */
+ (*lfun)(LOG_DEBUG, "rule exists %s", dbi.id);
+ (void)run_change("rem", &c, dbi.id, 0);
+ dbi.id[0] = '\0';
+ }
+ if (c.c_nfail != -1 && dbi.count >= c.c_nfail) {
+ int res = run_change("add", &c, dbi.id, sizeof(dbi.id));
+ if (res == -1)
+ goto out;
+ sockaddr_snprintf(rbuf, sizeof(rbuf), "%a",
+ (void *)&rss);
+ (*lfun)(LOG_INFO,
+ "blocked %s/%d:%d for %d seconds",
+ rbuf, c.c_lmask, c.c_port, c.c_duration);
+
+ }
+ break;
+ case BL_DELETE:
+ if (dbi.last == 0)
+ goto out;
+ dbi.last = 0;
+ break;
+ default:
+ (*lfun)(LOG_ERR, "unknown message %d", bi->bi_type);
+ }
+ if (state_put(state, &c, &dbi) == -1)
+ goto out;
+out:
+ close(bi->bi_fd);
+}
+
+static void
+update_interfaces(void)
+{
+ struct ifaddrs *oifas, *nifas;
+
+ if (getifaddrs(&nifas) == -1)
+ return;
+
+ oifas = ifas;
+ ifas = nifas;
+
+ if (oifas)
+ freeifaddrs(oifas);
+}
+
+static void
+update(void)
+{
+ struct timespec ts;
+ struct conf c;
+ struct dbinfo dbi;
+ unsigned int f, n;
+ char buf[128];
+ void *ss = &c.c_ss;
+
+ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
+ (*lfun)(LOG_ERR, "clock_gettime failed (%m)");
+ return;
+ }
+
+again:
+ for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1;
+ f = 0, n++)
+ {
+ time_t when = c.c_duration + dbi.last;
+ if (debug > 1) {
+ char b1[64], b2[64];
+ sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss);
+ (*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d "
+ "last=%s " "now=%s", __func__, n, buf, dbi.count,
+ c.c_duration, fmttime(b1, sizeof(b1), dbi.last),
+ fmttime(b2, sizeof(b2), ts.tv_sec));
+ }
+ if (c.c_duration == -1 || when >= ts.tv_sec)
+ continue;
+ if (dbi.id[0]) {
+ run_change("rem", &c, dbi.id, 0);
+ sockaddr_snprintf(buf, sizeof(buf), "%a", ss);
+ syslog(LOG_INFO, "released %s/%d:%d after %d seconds",
+ buf, c.c_lmask, c.c_port, c.c_duration);
+ }
+ state_del(state, &c);
+ goto again;
+ }
+}
+
+static void
+addfd(struct pollfd **pfdp, bl_t **blp, size_t *nfd, size_t *maxfd,
+ const char *path)
+{
+ bl_t bl = bl_create(true, path, vflag ? vdlog : vsyslog);
+ if (bl == NULL || !bl_isconnected(bl))
+ exit(EXIT_FAILURE);
+ if (*nfd >= *maxfd) {
+ *maxfd += 10;
+ *blp = realloc(*blp, sizeof(**blp) * *maxfd);
+ if (*blp == NULL)
+ err(EXIT_FAILURE, "malloc");
+ *pfdp = realloc(*pfdp, sizeof(**pfdp) * *maxfd);
+ if (*pfdp == NULL)
+ err(EXIT_FAILURE, "malloc");
+ }
+
+ (*pfdp)[*nfd].fd = bl_getfd(bl);
+ (*pfdp)[*nfd].events = POLLIN;
+ (*blp)[*nfd] = bl;
+ *nfd += 1;
+}
+
+static void
+uniqueadd(struct conf ***listp, size_t *nlist, size_t *mlist, struct conf *c)
+{
+ struct conf **list = *listp;
+
+ if (c->c_name[0] == '\0')
+ return;
+ for (size_t i = 0; i < *nlist; i++) {
+ if (strcmp(list[i]->c_name, c->c_name) == 0)
+ return;
+ }
+ if (*nlist == *mlist) {
+ *mlist += 10;
+ void *p = realloc(*listp, *mlist * sizeof(*list));
+ if (p == NULL)
+ err(EXIT_FAILURE, "Can't allocate for rule list");
+ list = *listp = p;
+ }
+ list[(*nlist)++] = c;
+}
+
+static void
+rules_flush(void)
+{
+ struct conf **list;
+ size_t nlist, mlist;
+
+ list = NULL;
+ mlist = nlist = 0;
+ for (size_t i = 0; i < rconf.cs_n; i++)
+ uniqueadd(&list, &nlist, &mlist, &rconf.cs_c[i]);
+ for (size_t i = 0; i < lconf.cs_n; i++)
+ uniqueadd(&list, &nlist, &mlist, &lconf.cs_c[i]);
+
+ for (size_t i = 0; i < nlist; i++)
+ run_flush(list[i]);
+ free(list);
+}
+
+static void
+rules_restore(void)
+{
+ struct conf c;
+ struct dbinfo dbi;
+ unsigned int f;
+
+ for (f = 1; state_iterate(state, &c, &dbi, f) == 1; f = 0) {
+ if (dbi.id[0] == '\0')
+ continue;
+ (void)run_change("rem", &c, dbi.id, 0);
+ (void)run_change("add", &c, dbi.id, sizeof(dbi.id));
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, tout, flags, flush, restore;
+ const char *spath, *blsock;
+
+ setprogname(argv[0]);
+
+ spath = NULL;
+ blsock = _PATH_BLSOCK;
+ flush = 0;
+ restore = 0;
+ tout = 0;
+ flags = O_RDWR|O_EXCL|O_CLOEXEC;
+ while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) {
+ switch (c) {
+ case 'C':
+ controlprog = optarg;
+ break;
+ case 'c':
+ configfile = optarg;
+ break;
+ case 'D':
+ dbfile = optarg;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'f':
+ flush++;
+ break;
+ case 'P':
+ spath = optarg;
+ break;
+ case 'R':
+ rulename = optarg;
+ break;
+ case 'r':
+ restore++;
+ break;
+ case 's':
+ blsock = optarg;
+ break;
+ case 't':
+ tout = atoi(optarg) * 1000;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage(c);
+ }
+ }
+
+ argc -= optind;
+ if (argc)
+ usage(0);
+
+ signal(SIGHUP, sighup);
+ signal(SIGINT, sigdone);
+ signal(SIGQUIT, sigdone);
+ signal(SIGTERM, sigdone);
+ signal(SIGUSR1, sigusr1);
+ signal(SIGUSR2, sigusr2);
+
+ openlog(getprogname(), LOG_PID, LOG_DAEMON);
+
+ if (debug) {
+ lfun = dlog;
+ if (tout == 0)
+ tout = 5000;
+ } else {
+ if (tout == 0)
+ tout = 15000;
+ }
+
+ update_interfaces();
+ conf_parse(configfile);
+ if (flush) {
+ rules_flush();
+ flags |= O_TRUNC;
+ }
+
+ if (restore)
+ rules_restore();
+
+ struct pollfd *pfd = NULL;
+ bl_t *bl = NULL;
+ size_t nfd = 0;
+ size_t maxfd = 0;
+
+ if (spath == NULL)
+ addfd(&pfd, &bl, &nfd, &maxfd, blsock);
+ else {
+ FILE *fp = fopen(spath, "r");
+ char *line;
+ if (fp == NULL)
+ err(EXIT_FAILURE, "Can't open `%s'", spath);
+ for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL;
+ free(line))
+ addfd(&pfd, &bl, &nfd, &maxfd, line);
+ fclose(fp);
+ }
+
+ state = state_open(dbfile, flags, 0600);
+ if (state == NULL)
+ state = state_open(dbfile, flags | O_CREAT, 0600);
+ if (state == NULL)
+ return EXIT_FAILURE;
+
+ if (!debug) {
+ if (daemon(0, 0) == -1)
+ err(EXIT_FAILURE, "daemon failed");
+ if (pidfile(NULL) == -1)
+ err(EXIT_FAILURE, "Can't create pidfile");
+ }
+
+ for (size_t t = 0; !done; t++) {
+ if (readconf) {
+ readconf = 0;
+ conf_parse(configfile);
+ }
+ switch (poll(pfd, (nfds_t)nfd, tout)) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ (*lfun)(LOG_ERR, "poll (%m)");
+ return EXIT_FAILURE;
+ case 0:
+ state_sync(state);
+ break;
+ default:
+ for (size_t i = 0; i < nfd; i++)
+ if (pfd[i].revents & POLLIN)
+ process(bl[i]);
+ }
+ if (t % 100 == 0)
+ state_sync(state);
+ if (t % 10000 == 0)
+ update_interfaces();
+ update();
+ }
+ state_close(state);
+ return 0;
+}
diff --git a/bin/blacklistd.conf.5 b/bin/blacklistd.conf.5
new file mode 100644
index 000000000000..40d507b3c08a
--- /dev/null
+++ b/bin/blacklistd.conf.5
@@ -0,0 +1,222 @@
+.\" $NetBSD: blacklistd.conf.5,v 1.3 2015/04/30 06:20:43 riz Exp $
+.\"
+.\" Copyright (c) 2015 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Christos Zoulas.
+.\"
+.\" 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``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 FOUNDATION OR CONTRIBUTORS
+.\" 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.
+.\"
+.Dd April 29, 2015
+.Dt BLACKLISTD.CONF 5
+.Os
+.Sh NAME
+.Nm blacklistd.conf
+.Nd configuration file format for blacklistd
+.Sh DESCRIPTION
+The
+.Nm
+files contains configuration lines for
+.Xr blacklistd 8 .
+It contains one entry per line, and is similar to
+.Xr inetd.conf 5 .
+There must be an entry for each field of the configuration file, with
+entries for each field separated by a tab or a space.
+Comments are denoted by a
+.Dq #
+at the beginning of a line.
+.Pp
+There are two kinds of configuration lines,
+.Va local
+and
+.Va remote .
+By default, configuration lines are
+.Va local ,
+i.e. the address specified refers to the addresses on the local machine.
+To switch to between
+.Va local
+and
+.Va remote
+configuration lines you can specify the stanzas:
+.Dq [local]
+and
+.Dq [remote] .
+.Pp
+On
+.Va local
+and
+.Va remote
+lines
+.Dq *
+means use the default, or wildcard match.
+In addition, for
+.Va remote
+lines
+.Dq =
+means use the values from the matched
+.Va local
+configuration line.
+.Pp
+The first four fields,
+.Va location ,
+.Va type ,
+.Va proto ,
+and
+.Va owner
+are used to match the
+.Va local
+or
+.Va remote
+addresses, whereas the last 3 fields
+.Va name ,
+.Va nfail ,
+and
+.Va disable
+are used to modify the filtering action.
+.Pp
+The first field denotes the
+.Va location
+as an address, mask, and port.
+The syntax for the
+.Va location
+is:
+.Bd -literal -offset indent
+ [<address>|<interface>][/<mask>][:<port>]
+.Ed
+.Pp
+The
+.Dv address
+can be an IPv4 address in numeric format, an IPv6 address
+in numeric format and enclosed by square brackets, or an interface name.
+Mask modifiers are not allowed on interfaces because interfaces
+have multiple address in different protocols where the mask has a different
+size.
+.Pp
+The
+.Dv mask
+is always numeric, but the
+.Dv port
+can be either numeric or symbolic.
+.Pp
+The second field is the socket
+.Va type :
+.Dv stream ,
+.Dv dgram ,
+or numeric.
+The third field is the
+.Va prococol :
+.Dv tcp ,
+.Dv udp ,
+.Dv tcp6 ,
+.Dv udp6 ,
+or numeric.
+The fourth file is the effective user
+.Va ( owner )
+of the daemon process reporting the event,
+either as a username or a userid.
+.Pp
+The rest of the fields are controlling the behavior of the filter.
+.Pp
+The
+.Va name
+field, is the name of the packet filter rule to be used.
+If the
+.Va name
+starts with a
+.Dq - ,
+then the default rulename is prepended to the given name.
+If the
+.Dv name
+contains a
+.Dq / ,
+the remaining portion of the name is interpreted as the mask to be
+applied to the address specified in the rule, so one can block whole
+subnets for a single rule violation.
+.Pp
+The
+.Va nfail
+field contains the number of failed attempts before access is blocked,
+defaulting to
+.Dq *
+meaning never, and the last field
+.Va disable
+specifies the amount of time since the last access that the blocking
+rule should be active, defaulting to
+.Dq *
+meaning forever.
+The default unit for
+.Va disable
+is seconds, but one can specify suffixes for different units, such as
+.Dq m
+for minutes
+.Dq h
+for hours and
+.Dq d
+for days.
+.Pp
+Matching is done first by checking the
+.Va local
+rules one by one, from the most specific to the least specific.
+If a match is found, then the
+.Va remote
+rules are applied, and if a match is found the
+.Va name ,
+.Va nfail ,
+and
+.Va disable
+fields can be altered by the
+.Va remote
+rule that matched.
+.Pp
+The
+.Va remote
+rules can be used for whitelisting specific addresses, changing the mask
+size, or the rule that the packet filter uses, the number of failed attempts,
+or the blocked duration.
+.Sh FILES
+.Bl -tag -width /etc/blacklistd.conf -compact
+.It Pa /etc/blacklistd.conf
+Configuration file.
+.El
+.Sh EXAMPLES
+.Bd -literal -offset
+# Block ssh, after 3 attempts for 6 hours on the bnx0 interface
+[local]
+# location type proto owner name nfail duration
+bnx0:ssh * * * * 3 6h
+[remote]
+# Never block 1.2.3.4
+1.2.3.4:ssh * * * * * *
+# For addresses coming from 8.8.0.0/16 block class C networks instead
+# individual hosts, but keep the rest of the blocking parameters the same.
+8.8.0.0/16:ssh * * * /24 = =
+.Ed
+.Sh SEE ALSO
+.Xr blacklistctl 8 ,
+.Xr blacklistd 8
+.Sh HISTORY
+.Nm
+appeared in
+.Nx 7 .
+.Sh AUTHORS
+.An Christos Zoulas
diff --git a/bin/conf.c b/bin/conf.c
new file mode 100644
index 000000000000..3ec1e085c276
--- /dev/null
+++ b/bin/conf.c
@@ -0,0 +1,1142 @@
+/* $NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 christos Exp $");
+
+#include <stdio.h>
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/socket.h>
+
+#include "bl.h"
+#include "internal.h"
+#include "support.h"
+#include "conf.h"
+
+
+struct sockaddr_if {
+ uint8_t sif_len;
+ sa_family_t sif_family;
+ in_port_t sif_port;
+ char sif_name[16];
+};
+
+#define SIF_NAME(a) \
+ ((const struct sockaddr_if *)(const void *)(a))->sif_name
+
+static int conf_is_interface(const char *);
+
+#define FSTAR -1
+#define FEQUAL -2
+
+static void
+advance(char **p)
+{
+ char *ep = *p;
+ while (*ep && !isspace((unsigned char)*ep))
+ ep++;
+ while (*ep && isspace((unsigned char)*ep))
+ *ep++ = '\0';
+ *p = ep;
+}
+
+static int
+getnum(const char *f, size_t l, bool local, void *rp, const char *name,
+ const char *p)
+{
+ int e;
+ intmax_t im;
+ int *r = rp;
+
+ if (strcmp(p, "*") == 0) {
+ *r = FSTAR;
+ return 0;
+ }
+ if (strcmp(p, "=") == 0) {
+ if (local)
+ goto out;
+ *r = FEQUAL;
+ return 0;
+ }
+
+ im = strtoi(p, NULL, 0, 0, INT_MAX, &e);
+ if (e == 0) {
+ *r = (int)im;
+ return 0;
+ }
+
+ if (f == NULL)
+ return -1;
+ (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l,
+ name, p);
+ return -1;
+out:
+ (*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config",
+ __func__, f, l, name);
+ return -1;
+
+}
+
+static int
+getnfail(const char *f, size_t l, bool local, struct conf *c, const char *p)
+{
+ return getnum(f, l, local, &c->c_nfail, "nfail", p);
+}
+
+static int
+getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p)
+{
+ int e;
+ char *ep;
+ intmax_t tot, im;
+
+ tot = 0;
+ if (strcmp(p, "*") == 0) {
+ c->c_duration = FSTAR;
+ return 0;
+ }
+ if (strcmp(p, "=") == 0) {
+ if (local)
+ goto out;
+ c->c_duration = FEQUAL;
+ return 0;
+ }
+again:
+ im = strtoi(p, &ep, 0, 0, INT_MAX, &e);
+
+ if (e == ENOTSUP) {
+ switch (*ep) {
+ case 'd':
+ im *= 24;
+ /*FALLTHROUGH*/
+ case 'h':
+ im *= 60;
+ /*FALLTHROUGH*/
+ case 'm':
+ im *= 60;
+ /*FALLTHROUGH*/
+ case 's':
+ e = 0;
+ tot += im;
+ if (ep[1] != '\0') {
+ p = ep + 2;
+ goto again;
+ }
+ break;
+ }
+ } else
+ tot = im;
+
+ if (e == 0) {
+ c->c_duration = (int)tot;
+ return 0;
+ }
+
+ if (f == NULL)
+ return -1;
+ (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p);
+ return -1;
+out:
+ (*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local"
+ " config", __func__, f, l);
+ return -1;
+
+}
+
+static int
+getport(const char *f, size_t l, bool local, void *r, const char *p)
+{
+ struct servent *sv;
+
+ // XXX: Pass in the proto instead
+ if ((sv = getservbyname(p, "tcp")) != NULL) {
+ *(int *)r = ntohs(sv->s_port);
+ return 0;
+ }
+ if ((sv = getservbyname(p, "udp")) != NULL) {
+ *(int *)r = ntohs(sv->s_port);
+ return 0;
+ }
+
+ return getnum(f, l, local, r, "service", p);
+}
+
+static int
+getmask(const char *f, size_t l, bool local, const char **p, int *mask)
+{
+ char *d;
+ const char *s = *p;
+
+ if ((d = strchr(s, ':')) != NULL) {
+ *d++ = '\0';
+ *p = d;
+ }
+ if ((d = strchr(s, '/')) == NULL) {
+ *mask = FSTAR;
+ return 0;
+ }
+
+ *d++ = '\0';
+ return getnum(f, l, local, mask, "mask", d);
+}
+
+static int
+gethostport(const char *f, size_t l, bool local, struct conf *c, const char *p)
+{
+ char *d; // XXX: Ok to write to string.
+ in_port_t *port = NULL;
+ const char *pstr;
+
+ if (strcmp(p, "*") == 0) {
+ c->c_port = FSTAR;
+ c->c_lmask = FSTAR;
+ return 0;
+ }
+
+ if ((d = strchr(p, ']')) != NULL) {
+ *d++ = '\0';
+ pstr = d;
+ p++;
+ } else
+ pstr = p;
+
+ if (getmask(f, l, local, &pstr, &c->c_lmask) == -1)
+ goto out;
+
+ if (d) {
+ struct sockaddr_in6 *sin6 = (void *)&c->c_ss;
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p);
+ if (strcmp(p, "*") != 0) {
+ if (inet_pton(AF_INET6, p, &sin6->sin6_addr) == -1)
+ goto out;
+ sin6->sin6_family = AF_INET6;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ sin6->sin6_len = sizeof(*sin6);
+#endif
+ port = &sin6->sin6_port;
+ }
+ } else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) {
+ if (pstr == p)
+ pstr = "*";
+ struct sockaddr_in *sin = (void *)&c->c_ss;
+ struct sockaddr_if *sif = (void *)&c->c_ss;
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p);
+ if (strcmp(p, "*") != 0) {
+ if (conf_is_interface(p)) {
+ if (!local)
+ goto out2;
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s: interface %s",
+ __func__, p);
+ if (c->c_lmask != FSTAR)
+ goto out1;
+ sif->sif_family = AF_MAX;
+ strlcpy(sif->sif_name, p,
+ sizeof(sif->sif_name));
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ sif->sif_len = sizeof(*sif);
+#endif
+ port = &sif->sif_port;
+ } else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1)
+ {
+ sin->sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ sin->sin_len = sizeof(*sin);
+#endif
+ port = &sin->sin_port;
+ } else
+ goto out;
+ }
+ }
+
+ if (getport(f, l, local, &c->c_port, pstr) == -1)
+ return -1;
+
+ if (port && c->c_port != FSTAR && c->c_port != FEQUAL)
+ *port = htons((in_port_t)c->c_port);
+ return 0;
+out:
+ (*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, pstr);
+ return -1;
+out1:
+ (*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with "
+ "interface [%s]", __func__, f, l, c->c_lmask, p);
+ return -1;
+out2:
+ (*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense "
+ "with remote config [%s]", __func__, f, l, p);
+ return -1;
+}
+
+static int
+getproto(const char *f, size_t l, bool local __unused, struct conf *c,
+ const char *p)
+{
+ if (strcmp(p, "stream") == 0) {
+ c->c_proto = IPPROTO_TCP;
+ return 0;
+ }
+ if (strcmp(p, "dgram") == 0) {
+ c->c_proto = IPPROTO_UDP;
+ return 0;
+ }
+ return getnum(f, l, local, &c->c_proto, "protocol", p);
+}
+
+static int
+getfamily(const char *f, size_t l, bool local __unused, struct conf *c,
+ const char *p)
+{
+ if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) {
+ c->c_family = p[3] == '6' ? AF_INET6 : AF_INET;
+ return 0;
+ }
+ return getnum(f, l, local, &c->c_family, "family", p);
+}
+
+static int
+getuid(const char *f, size_t l, bool local __unused, struct conf *c,
+ const char *p)
+{
+ struct passwd *pw;
+
+ if ((pw = getpwnam(p)) != NULL) {
+ c->c_uid = (int)pw->pw_uid;
+ return 0;
+ }
+
+ return getnum(f, l, local, &c->c_uid, "user", p);
+}
+
+
+static int
+getname(const char *f, size_t l, bool local, struct conf *c,
+ const char *p)
+{
+ if (getmask(f, l, local, &p, &c->c_rmask) == -1)
+ return -1;
+
+ if (strcmp(p, "*") == 0) {
+ strlcpy(c->c_name, rulename, CONFNAMESZ);
+ return 0;
+ }
+ if (strcmp(p, "=") == 0) {
+ if (local)
+ goto out;
+ c->c_name[0] = '\0';
+ return 0;
+ }
+
+ snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p);
+ return 0;
+out:
+ (*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local"
+ " config", __func__, f, l);
+ return -1;
+}
+
+static int
+getvalue(const char *f, size_t l, bool local, void *r, char **p,
+ int (*fun)(const char *, size_t, bool, struct conf *, const char *))
+{
+ char *ep = *p;
+
+ advance(p);
+ return (*fun)(f, l, local, r, ep);
+}
+
+
+static int
+conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local)
+{
+ int e;
+
+ while (*p && isspace((unsigned char)*p))
+ p++;
+
+ memset(c, 0, sizeof(*c));
+ e = getvalue(f, l, local, c, &p, gethostport);
+ if (e) return -1;
+ e = getvalue(f, l, local, c, &p, getproto);
+ if (e) return -1;
+ e = getvalue(f, l, local, c, &p, getfamily);
+ if (e) return -1;
+ e = getvalue(f, l, local, c, &p, getuid);
+ if (e) return -1;
+ e = getvalue(f, l, local, c, &p, getname);
+ if (e) return -1;
+ e = getvalue(f, l, local, c, &p, getnfail);
+ if (e) return -1;
+ e = getvalue(f, l, local, c, &p, getsecs);
+ if (e) return -1;
+
+ return 0;
+}
+
+static int
+conf_sort(const void *v1, const void *v2)
+{
+ const struct conf *c1 = v1;
+ const struct conf *c2 = v2;
+
+#define CMP(a, b, f) \
+ if ((a)->f > (b)->f) return -1; \
+ else if ((a)->f < (b)->f) return 1
+
+ CMP(c1, c2, c_ss.ss_family);
+ CMP(c1, c2, c_lmask);
+ CMP(c1, c2, c_port);
+ CMP(c1, c2, c_proto);
+ CMP(c1, c2, c_family);
+ CMP(c1, c2, c_rmask);
+ CMP(c1, c2, c_uid);
+#undef CMP
+ return 0;
+}
+
+static int
+conf_is_interface(const char *name)
+{
+ const struct ifaddrs *ifa;
+
+ for (ifa = ifas; ifa; ifa = ifa->ifa_next)
+ if (strcmp(ifa->ifa_name, name) == 0)
+ return 1;
+ return 0;
+}
+
+#define MASK(m) ((uint32_t)~((1 << (32 - (m))) - 1))
+
+static int
+conf_amask_eq(const void *v1, const void *v2, size_t len, int mask)
+{
+ const uint32_t *a1 = v1;
+ const uint32_t *a2 = v2;
+ uint32_t m;
+ int omask = mask;
+
+ len >>= 2;
+ switch (mask) {
+ case FSTAR:
+ if (memcmp(v1, v2, len) == 0)
+ return 1;
+ goto out;
+ case FEQUAL:
+
+ (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
+ mask);
+ abort();
+ default:
+ break;
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ if (mask > 32) {
+ m = htonl((uint32_t)~0);
+ mask -= 32;
+ } else if (mask) {
+ m = htonl(MASK(mask));
+ mask = 0;
+ } else
+ return 1;
+ if ((a1[i] & m) != (a2[i] & m))
+ goto out;
+ }
+ return 1;
+out:
+ if (debug > 1) {
+ char b1[256], b2[256];
+ len <<= 2;
+ blhexdump(b1, sizeof(b1), "a1", v1, len);
+ blhexdump(b2, sizeof(b2), "a2", v2, len);
+ (*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__,
+ b1, b2, omask);
+ }
+ return 0;
+}
+
+/*
+ * Apply the mask to the given address
+ */
+static void
+conf_apply_mask(void *v, size_t len, int mask)
+{
+ uint32_t *a = v;
+ uint32_t m;
+
+ switch (mask) {
+ case FSTAR:
+ return;
+ case FEQUAL:
+ (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
+ mask);
+ abort();
+ default:
+ break;
+ }
+ len >>= 2;
+
+ for (size_t i = 0; i < len; i++) {
+ if (mask > 32) {
+ m = htonl((uint32_t)~0);
+ mask -= 32;
+ } else if (mask) {
+ m = htonl(MASK(mask));
+ mask = 0;
+ } else
+ m = 0;
+ a[i] &= m;
+ }
+}
+
+/*
+ * apply the mask and the port to the address given
+ */
+static void
+conf_addr_set(struct conf *c, const struct sockaddr_storage *ss)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ in_port_t *port;
+ void *addr;
+ size_t alen;
+
+ c->c_lmask = c->c_rmask;
+ c->c_ss = *ss;
+
+ if (c->c_ss.ss_family != c->c_family) {
+ (*lfun)(LOG_CRIT, "%s: Internal error: mismatched family "
+ "%u != %u", __func__, c->c_ss.ss_family, c->c_family);
+ abort();
+ }
+
+ switch (c->c_ss.ss_family) {
+ case AF_INET:
+ sin = (void *)&c->c_ss;
+ port = &sin->sin_port;
+ addr = &sin->sin_addr;
+ alen = sizeof(sin->sin_addr);
+ break;
+ case AF_INET6:
+ sin6 = (void *)&c->c_ss;
+ port = &sin6->sin6_port;
+ addr = &sin6->sin6_addr;
+ alen = sizeof(sin6->sin6_addr);
+ break;
+ default:
+ (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
+ __func__, c->c_ss.ss_family);
+ abort();
+ }
+
+ *port = htons((in_port_t)c->c_port);
+ conf_apply_mask(addr, alen, c->c_lmask);
+ if (c->c_lmask == FSTAR)
+ c->c_lmask = (int)(alen * 8);
+ if (debug) {
+ char buf[128];
+ sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss);
+ (*lfun)(LOG_DEBUG, "Applied address %s", buf);
+ }
+}
+
+/*
+ * Compared two addresses for equality applying the mask
+ */
+static int
+conf_inet_eq(const void *v1, const void *v2, int mask)
+{
+ const struct sockaddr *sa1 = v1;
+ const struct sockaddr *sa2 = v2;
+ size_t size;
+
+ if (sa1->sa_family != sa2->sa_family)
+ return 0;
+
+ switch (sa1->sa_family) {
+ case AF_INET: {
+ const struct sockaddr_in *s1 = v1;
+ const struct sockaddr_in *s2 = v2;
+ size = sizeof(s1->sin_addr);
+ v1 = &s1->sin_addr;
+ v2 = &s2->sin_addr;
+ break;
+ }
+
+ case AF_INET6: {
+ const struct sockaddr_in6 *s1 = v1;
+ const struct sockaddr_in6 *s2 = v2;
+ size = sizeof(s1->sin6_addr);
+ v1 = &s1->sin6_addr;
+ v2 = &s2->sin6_addr;
+ break;
+ }
+
+ default:
+ (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
+ __func__, sa1->sa_family);
+ abort();
+ }
+
+ return conf_amask_eq(v1, v2, size, mask);
+}
+
+static int
+conf_addr_in_interface(const struct sockaddr_storage *s1,
+ const struct sockaddr_storage *s2, int mask)
+{
+ const char *name = SIF_NAME(s2);
+ const struct ifaddrs *ifa;
+
+ for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_UP) == 0)
+ continue;
+
+ if (strcmp(ifa->ifa_name, name) != 0)
+ continue;
+
+ if (s1->ss_family != ifa->ifa_addr->sa_family)
+ continue;
+
+ bool eq;
+ switch (s1->ss_family) {
+ case AF_INET:
+ case AF_INET6:
+ eq = conf_inet_eq(ifa->ifa_addr, s1, mask);
+ break;
+ default:
+ (*lfun)(LOG_ERR, "Bad family %u", s1->ss_family);
+ continue;
+ }
+ if (eq)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+conf_addr_eq(const struct sockaddr_storage *s1,
+ const struct sockaddr_storage *s2, int mask)
+{
+ switch (s2->ss_family) {
+ case 0:
+ return 1;
+ case AF_MAX:
+ return conf_addr_in_interface(s1, s2, mask);
+ case AF_INET:
+ case AF_INET6:
+ return conf_inet_eq(s1, s2, mask);
+ default:
+ (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
+ __func__, s1->ss_family);
+ abort();
+ }
+}
+
+static int
+conf_eq(const struct conf *c1, const struct conf *c2)
+{
+
+ if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
+ return 0;
+
+#define CMP(a, b, f) \
+ if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \
+ if (debug > 1) \
+ (*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \
+ __STRING(f), (a)->f, (b)->f); \
+ return 0; \
+ }
+ CMP(c1, c2, c_port);
+ CMP(c1, c2, c_proto);
+ CMP(c1, c2, c_family);
+ CMP(c1, c2, c_uid);
+#undef CMP
+ return 1;
+}
+
+static const char *
+conf_num(char *b, size_t l, int n)
+{
+ switch (n) {
+ case FSTAR:
+ return "*";
+ case FEQUAL:
+ return "=";
+ default:
+ snprintf(b, l, "%d", n);
+ return b;
+ }
+}
+
+static const char *
+fmtname(const char *n) {
+ size_t l = strlen(rulename);
+ if (l == 0)
+ return "*";
+ if (strncmp(n, rulename, l) == 0) {
+ if (n[l] != '\0')
+ return n + l;
+ else
+ return "*";
+ } else if (!*n)
+ return "=";
+ else
+ return n;
+}
+
+static void
+fmtport(char *b, size_t l, int port)
+{
+ char buf[128];
+
+ if (port == FSTAR)
+ return;
+
+ if (b[0] == '\0' || strcmp(b, "*") == 0)
+ snprintf(b, l, "%d", port);
+ else {
+ snprintf(buf, sizeof(buf), ":%d", port);
+ strlcat(b, buf, l);
+ }
+}
+
+static const char *
+fmtmask(char *b, size_t l, int fam, int mask)
+{
+ char buf[128];
+
+ switch (mask) {
+ case FSTAR:
+ return "";
+ case FEQUAL:
+ if (strcmp(b, "=") == 0)
+ return "";
+ else {
+ strlcat(b, "/=", l);
+ return b;
+ }
+ default:
+ break;
+ }
+
+ switch (fam) {
+ case AF_INET:
+ if (mask == 32)
+ return "";
+ break;
+ case AF_INET6:
+ if (mask == 128)
+ return "";
+ break;
+ default:
+ break;
+ }
+
+ snprintf(buf, sizeof(buf), "/%d", mask);
+ strlcat(b, buf, l);
+ return b;
+}
+
+static const char *
+conf_namemask(char *b, size_t l, const struct conf *c)
+{
+ strlcpy(b, fmtname(c->c_name), l);
+ fmtmask(b, l, c->c_family, c->c_rmask);
+ return b;
+}
+
+const char *
+conf_print(char *buf, size_t len, const char *pref, const char *delim,
+ const struct conf *c)
+{
+ char ha[128], hb[32], b[5][64];
+ int sp;
+
+#define N(n, v) conf_num(b[n], sizeof(b[n]), (v))
+
+ switch (c->c_ss.ss_family) {
+ case 0:
+ snprintf(ha, sizeof(ha), "*");
+ break;
+ case AF_MAX:
+ snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss));
+ break;
+ default:
+ sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss);
+ break;
+ }
+
+ fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask);
+ fmtport(ha, sizeof(ha), c->c_port);
+
+ sp = *delim == '\t' ? 20 : -1;
+ hb[0] = '\0';
+ if (*delim)
+ snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s"
+ "%s%s" "%s%s%s",
+ pref, sp, sp, ha, delim, N(0, c->c_proto), delim,
+ N(1, c->c_family), delim, N(2, c->c_uid), delim,
+ conf_namemask(hb, sizeof(hb), c), delim,
+ N(3, c->c_nfail), delim, N(4, c->c_duration));
+ else
+ snprintf(buf, len, "%starget:%s, proto:%s, family:%s, "
+ "uid:%s, name:%s, nfail:%s, duration:%s", pref,
+ ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid),
+ conf_namemask(hb, sizeof(hb), c),
+ N(3, c->c_nfail), N(4, c->c_duration));
+ return buf;
+}
+
+/*
+ * Apply the local config match to the result
+ */
+static void
+conf_apply(struct conf *c, const struct conf *sc)
+{
+ char buf[BUFSIZ];
+
+ if (debug) {
+ (*lfun)(LOG_DEBUG, "%s: %s", __func__,
+ conf_print(buf, sizeof(buf), "merge:\t", "", sc));
+ (*lfun)(LOG_DEBUG, "%s: %s", __func__,
+ conf_print(buf, sizeof(buf), "to:\t", "", c));
+ }
+ memcpy(c->c_name, sc->c_name, CONFNAMESZ);
+ c->c_uid = sc->c_uid;
+ c->c_rmask = sc->c_rmask;
+ c->c_nfail = sc->c_nfail;
+ c->c_duration = sc->c_duration;
+
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s: %s", __func__,
+ conf_print(buf, sizeof(buf), "result:\t", "", c));
+}
+
+/*
+ * Merge a remote configuration to the result
+ */
+static void
+conf_merge(struct conf *c, const struct conf *sc)
+{
+ char buf[BUFSIZ];
+
+ if (debug) {
+ (*lfun)(LOG_DEBUG, "%s: %s", __func__,
+ conf_print(buf, sizeof(buf), "merge:\t", "", sc));
+ (*lfun)(LOG_DEBUG, "%s: %s", __func__,
+ conf_print(buf, sizeof(buf), "to:\t", "", c));
+ }
+
+ if (sc->c_name[0])
+ memcpy(c->c_name, sc->c_name, CONFNAMESZ);
+ if (sc->c_uid != FEQUAL)
+ c->c_uid = sc->c_uid;
+ if (sc->c_rmask != FEQUAL)
+ c->c_lmask = c->c_rmask = sc->c_rmask;
+ if (sc->c_nfail != FEQUAL)
+ c->c_nfail = sc->c_nfail;
+ if (sc->c_duration != FEQUAL)
+ c->c_duration = sc->c_duration;
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s: %s", __func__,
+ conf_print(buf, sizeof(buf), "result:\t", "", c));
+}
+
+static void
+confset_init(struct confset *cs)
+{
+ cs->cs_c = NULL;
+ cs->cs_n = 0;
+ cs->cs_m = 0;
+}
+
+static int
+confset_grow(struct confset *cs)
+{
+ void *tc;
+
+ cs->cs_m += 10;
+ tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c));
+ if (tc == NULL) {
+ (*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__);
+ return -1;
+ }
+ cs->cs_c = tc;
+ return 0;
+}
+
+static struct conf *
+confset_get(struct confset *cs)
+{
+ return &cs->cs_c[cs->cs_n];
+}
+
+static bool
+confset_full(const struct confset *cs)
+{
+ return cs->cs_n == cs->cs_m;
+}
+
+static void
+confset_sort(struct confset *cs)
+{
+ qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort);
+}
+
+static void
+confset_add(struct confset *cs)
+{
+ cs->cs_n++;
+}
+
+static void
+confset_free(struct confset *cs)
+{
+ free(cs->cs_c);
+ confset_init(cs);
+}
+
+static void
+confset_replace(struct confset *dc, struct confset *sc)
+{
+ struct confset tc;
+ tc = *dc;
+ *dc = *sc;
+ confset_init(sc);
+ confset_free(&tc);
+}
+
+static void
+confset_list(const struct confset *cs, const char *msg, const char *where)
+{
+ char buf[BUFSIZ];
+
+ (*lfun)(LOG_DEBUG, "[%s]", msg);
+ (*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration",
+ where);
+ for (size_t i = 0; i < cs->cs_n; i++)
+ (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t",
+ &cs->cs_c[i]));
+}
+
+/*
+ * Match a configuration against the given list and apply the function
+ * to it, returning the matched entry number.
+ */
+static size_t
+confset_match(const struct confset *cs, struct conf *c,
+ void (*fun)(struct conf *, const struct conf *))
+{
+ char buf[BUFSIZ];
+ size_t i;
+
+ for (i = 0; i < cs->cs_n; i++) {
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
+ "check:\t", "", &cs->cs_c[i]));
+ if (conf_eq(c, &cs->cs_c[i])) {
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s",
+ conf_print(buf, sizeof(buf),
+ "found:\t", "", &cs->cs_c[i]));
+ (*fun)(c, &cs->cs_c[i]);
+ break;
+ }
+ }
+ return i;
+}
+
+const struct conf *
+conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss,
+ struct conf *cr)
+{
+ int proto;
+ socklen_t slen;
+ struct sockaddr_storage lss;
+ size_t i;
+ char buf[BUFSIZ];
+
+ memset(cr, 0, sizeof(*cr));
+ slen = sizeof(lss);
+ memset(&lss, 0, slen);
+ if (getsockname(fd, (void *)&lss, &slen) == -1) {
+ (*lfun)(LOG_ERR, "getsockname failed (%m)");
+ return NULL;
+ }
+
+ slen = sizeof(proto);
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) {
+ (*lfun)(LOG_ERR, "getsockopt failed (%m)");
+ return NULL;
+ }
+
+ if (debug) {
+ sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&lss);
+ (*lfun)(LOG_DEBUG, "listening socket: %s", buf);
+ }
+
+ switch (proto) {
+ case SOCK_STREAM:
+ cr->c_proto = IPPROTO_TCP;
+ break;
+ case SOCK_DGRAM:
+ cr->c_proto = IPPROTO_UDP;
+ break;
+ default:
+ (*lfun)(LOG_ERR, "unsupported protocol %d", proto);
+ return NULL;
+ }
+
+ switch (lss.ss_family) {
+ case AF_INET:
+ cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port);
+ break;
+ case AF_INET6:
+ cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port);
+ break;
+ default:
+ (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family);
+ return NULL;
+ }
+
+ cr->c_ss = lss;
+ cr->c_lmask = FSTAR;
+ cr->c_uid = (int)uid;
+ cr->c_family = lss.ss_family;
+ cr->c_name[0] = '\0';
+ cr->c_rmask = FSTAR;
+ cr->c_nfail = FSTAR;
+ cr->c_duration = FSTAR;
+
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
+ "look:\t", "", cr));
+
+ /* match the local config */
+ i = confset_match(&lconf, cr, conf_apply);
+ if (i == lconf.cs_n) {
+ if (debug)
+ (*lfun)(LOG_DEBUG, "not found");
+ return NULL;
+ }
+
+ conf_addr_set(cr, rss);
+ /* match the remote config */
+ confset_match(&rconf, cr, conf_merge);
+ /* to apply the mask */
+ conf_addr_set(cr, &cr->c_ss);
+
+ return cr;
+}
+
+
+void
+conf_parse(const char *f)
+{
+ FILE *fp;
+ char *line;
+ size_t lineno, len;
+ struct confset lc, rc, *cs;
+
+ if ((fp = fopen(f, "r")) == NULL) {
+ (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, f);
+ return;
+ }
+
+ lineno = 1;
+
+ confset_init(&rc);
+ confset_init(&lc);
+ cs = &lc;
+ for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL;
+ free(line))
+ {
+ if (!*line)
+ continue;
+ if (strcmp(line, "[local]") == 0) {
+ cs = &lc;
+ continue;
+ }
+ if (strcmp(line, "[remote]") == 0) {
+ cs = &rc;
+ continue;
+ }
+
+ if (confset_full(cs)) {
+ if (confset_grow(cs) == -1) {
+ confset_free(&lc);
+ confset_free(&rc);
+ fclose(fp);
+ return;
+ }
+ }
+ if (conf_parseline(f, lineno, line, confset_get(cs),
+ cs == &lc) == -1)
+ continue;
+ confset_add(cs);
+ }
+
+ fclose(fp);
+ confset_sort(&lc);
+ confset_sort(&rc);
+
+ confset_replace(&rconf, &rc);
+ confset_replace(&lconf, &lc);
+
+ if (debug) {
+ confset_list(&lconf, "local", "target");
+ confset_list(&rconf, "remote", "source");
+ }
+}
diff --git a/bin/conf.h b/bin/conf.h
new file mode 100644
index 000000000000..03f1942e3e32
--- /dev/null
+++ b/bin/conf.h
@@ -0,0 +1,65 @@
+/* $NetBSD: conf.h,v 1.6 2015/01/27 19:40:36 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _CONF_H
+#define _CONF_H
+
+#include <sys/socket.h>
+
+struct conf {
+ struct sockaddr_storage c_ss;
+ int c_lmask;
+ int c_port;
+ int c_proto;
+ int c_family;
+ int c_uid;
+ int c_nfail;
+ char c_name[128];
+ int c_rmask;
+ int c_duration;
+};
+
+struct confset {
+ struct conf *cs_c;
+ size_t cs_n;
+ size_t cs_m;
+};
+
+#define CONFNAMESZ sizeof(((struct conf *)0)->c_name)
+
+__BEGIN_DECLS
+const char *conf_print(char *, size_t, const char *, const char *,
+ const struct conf *);
+void conf_parse(const char *);
+const struct conf *conf_find(int, uid_t, const struct sockaddr_storage *,
+ struct conf *);
+__END_DECLS
+
+#endif /* _CONF_H */
diff --git a/bin/internal.c b/bin/internal.c
new file mode 100644
index 000000000000..5c039e4dc5d2
--- /dev/null
+++ b/bin/internal.c
@@ -0,0 +1,48 @@
+/* $NetBSD: internal.c,v 1.5 2015/01/27 19:40:37 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: internal.c,v 1.5 2015/01/27 19:40:37 christos Exp $");
+
+#include <stdio.h>
+#include <syslog.h>
+#include "conf.h"
+#include "internal.h"
+
+int debug;
+const char *rulename = "blacklistd";
+const char *controlprog = _PATH_BLCONTROL;
+struct confset lconf, rconf;
+struct ifaddrs *ifas;
+void (*lfun)(int, const char *, ...) = syslog;
diff --git a/bin/internal.h b/bin/internal.h
new file mode 100644
index 000000000000..5a40e49fbbd5
--- /dev/null
+++ b/bin/internal.h
@@ -0,0 +1,57 @@
+/* $NetBSD: internal.h,v 1.14 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _INTERNAL_H
+#define _INTERNAL_H
+
+#ifndef _PATH_BLCONF
+#define _PATH_BLCONF "/etc/blacklistd.conf"
+#endif
+#ifndef _PATH_BLCONTROL
+#define _PATH_BLCONTROL "/libexec/blacklistd-helper"
+#endif
+#ifndef _PATH_BLSTATE
+#define _PATH_BLSTATE "/var/db/blacklistd.db"
+#endif
+
+extern struct confset rconf, lconf;
+extern int debug;
+extern const char *rulename;
+extern const char *controlprog;
+extern struct ifaddrs *ifas;
+
+#if !defined(__syslog_attribute__) && !defined(__syslog__)
+#define __syslog__ __printf__
+#endif
+
+extern void (*lfun)(int, const char *, ...)
+ __attribute__((__format__(__syslog__, 2, 3)));
+
+#endif /* _INTERNAL_H */
diff --git a/bin/run.c b/bin/run.c
new file mode 100644
index 000000000000..8499edd3d9c2
--- /dev/null
+++ b/bin/run.c
@@ -0,0 +1,156 @@
+/* $NetBSD: run.c,v 1.14 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: run.c,v 1.14 2016/04/04 15:52:56 christos Exp $");
+
+#include <stdio.h>
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#include <stdarg.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <syslog.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include "run.h"
+#include "conf.h"
+#include "internal.h"
+#include "support.h"
+
+extern char **environ;
+
+static char *
+run(const char *cmd, const char *name, ...)
+{
+ const char *argv[20];
+ size_t i;
+ va_list ap;
+ FILE *fp;
+ char buf[10240], *res;
+
+ argv[0] = "control";
+ argv[1] = cmd;
+ argv[2] = name;
+ va_start(ap, name);
+ for (i = 3; i < __arraycount(argv) &&
+ (argv[i] = va_arg(ap, char *)) != NULL; i++)
+ continue;
+ va_end(ap);
+
+ if (debug) {
+ size_t z;
+ int r;
+
+ r = snprintf(buf, sizeof(buf), "run %s [", controlprog);
+ if (r == -1 || (z = (size_t)r) >= sizeof(buf))
+ z = sizeof(buf);
+ for (i = 0; argv[i]; i++) {
+ r = snprintf(buf + z, sizeof(buf) - z, "%s%s",
+ argv[i], argv[i + 1] ? " " : "");
+ if (r == -1 || (z += (size_t)r) >= sizeof(buf))
+ z = sizeof(buf);
+ }
+ (*lfun)(LOG_DEBUG, "%s]", buf);
+ }
+
+ fp = popenve(controlprog, __UNCONST(argv), environ, "r");
+ if (fp == NULL) {
+ (*lfun)(LOG_ERR, "popen %s failed (%m)", controlprog);
+ return NULL;
+ }
+ if (fgets(buf, sizeof(buf), fp) != NULL)
+ res = strdup(buf);
+ else
+ res = NULL;
+ pclose(fp);
+ if (debug)
+ (*lfun)(LOG_DEBUG, "%s returns %s", cmd, res);
+ return res;
+}
+
+void
+run_flush(const struct conf *c)
+{
+ free(run("flush", c->c_name, NULL));
+}
+
+int
+run_change(const char *how, const struct conf *c, char *id, size_t len)
+{
+ const char *prname;
+ char poname[64], adname[128], maskname[32], *rv;
+ size_t off;
+
+ switch (c->c_proto) {
+ case -1:
+ prname = "";
+ break;
+ case IPPROTO_TCP:
+ prname = "tcp";
+ break;
+ case IPPROTO_UDP:
+ prname = "udp";
+ break;
+ default:
+ (*lfun)(LOG_ERR, "%s: bad protocol %d", __func__, c->c_proto);
+ return -1;
+ }
+
+ if (c->c_port != -1)
+ snprintf(poname, sizeof(poname), "%d", c->c_port);
+ else
+ poname[0] = '\0';
+
+ snprintf(maskname, sizeof(maskname), "%d", c->c_lmask);
+ sockaddr_snprintf(adname, sizeof(adname), "%a", (const void *)&c->c_ss);
+
+ rv = run(how, c->c_name, prname, adname, maskname, poname, id, NULL);
+ if (rv == NULL)
+ return -1;
+ if (len != 0) {
+ rv[strcspn(rv, "\n")] = '\0';
+ off = strncmp(rv, "OK ", 3) == 0 ? 3 : 0;
+ strlcpy(id, rv + off, len);
+ }
+ free(rv);
+ return 0;
+}
diff --git a/bin/run.h b/bin/run.h
new file mode 100644
index 000000000000..bafc3e554690
--- /dev/null
+++ b/bin/run.h
@@ -0,0 +1,41 @@
+/* $NetBSD: run.h,v 1.5 2015/01/27 19:40:37 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _RUN_H
+#define _RUN_H
+
+__BEGIN_DECLS
+struct conf;
+void run_flush(const struct conf *);
+struct sockaddr_storage;
+int run_change(const char *, const struct conf *, char *, size_t);
+__END_DECLS
+
+#endif /* _RUN_H */
diff --git a/bin/state.c b/bin/state.c
new file mode 100644
index 000000000000..eb97e970fb1e
--- /dev/null
+++ b/bin/state.c
@@ -0,0 +1,233 @@
+/* $NetBSD: state.c,v 1.18 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: state.c,v 1.18 2016/04/04 15:52:56 christos Exp $");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netinet/in.h>
+
+#include "bl.h"
+#include "internal.h"
+#include "conf.h"
+#include "support.h"
+#include "state.h"
+
+static HASHINFO openinfo = {
+ 4096, /* bsize */
+ 32, /* ffactor */
+ 256, /* nelem */
+ 8 * 1024 * 1024,/* cachesize */
+ NULL, /* hash() */
+ 0 /* lorder */
+};
+
+int
+state_close(DB *db)
+{
+ if (db == NULL)
+ return -1;
+ if ((*db->close)(db) == -1) {
+ (*lfun)(LOG_ERR, "%s: can't close db (%m)", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+DB *
+state_open(const char *dbname, int flags, mode_t perm)
+{
+ DB *db;
+
+#ifdef __APPLE__
+ flags &= O_CREAT|O_EXCL|O_EXLOCK|O_NONBLOCK|O_RDONLY|
+ O_RDWR|O_SHLOCK|O_TRUNC;
+#endif
+ db = dbopen(dbname, flags, perm, DB_HASH, &openinfo);
+ if (db == NULL) {
+ if (errno == ENOENT && (flags & O_CREAT) == 0)
+ return NULL;
+ (*lfun)(LOG_ERR, "%s: can't open `%s' (%m)", __func__, dbname);
+ }
+ return db;
+}
+
+static int
+state_sizecheck(const DBT *t)
+{
+ if (sizeof(struct conf) == t->size)
+ return 0;
+ (*lfun)(LOG_ERR, "Key size mismatch %zu != %zu", sizeof(struct conf),
+ t->size);
+ return -1;
+}
+
+static void
+dumpkey(const struct conf *k)
+{
+ char buf[10240];
+ blhexdump(buf, sizeof(buf), __func__, k, sizeof(*k));
+ (*lfun)(LOG_DEBUG, "%s", buf);
+ (*lfun)(LOG_DEBUG, "%s: %s", __func__,
+ conf_print(buf, sizeof(buf), "", "", k));
+
+}
+
+int
+state_del(DB *db, const struct conf *c)
+{
+ int rv;
+ DBT k;
+
+ if (db == NULL)
+ return -1;
+
+ k.data = __UNCONST(c);
+ k.size = sizeof(*c);
+
+ switch (rv = (*db->del)(db, &k, 0)) {
+ case 0:
+ case 1:
+ if (debug > 1) {
+ (*lfun)(LOG_DEBUG, "%s: returns %d", __func__, rv);
+ (*db->sync)(db, 0);
+ }
+ return 0;
+ default:
+ (*lfun)(LOG_ERR, "%s: failed (%m)", __func__);
+ return -1;
+ }
+}
+
+int
+state_get(DB *db, const struct conf *c, struct dbinfo *dbi)
+{
+ int rv;
+ DBT k, v;
+
+ if (db == NULL)
+ return -1;
+
+ k.data = __UNCONST(c);
+ k.size = sizeof(*c);
+
+ switch (rv = (*db->get)(db, &k, &v, 0)) {
+ case 0:
+ case 1:
+ if (rv)
+ memset(dbi, 0, sizeof(*dbi));
+ else
+ memcpy(dbi, v.data, sizeof(*dbi));
+ if (debug > 1)
+ (*lfun)(LOG_DEBUG, "%s: returns %d", __func__, rv);
+ return 0;
+ default:
+ (*lfun)(LOG_ERR, "%s: failed (%m)", __func__);
+ return -1;
+ }
+}
+
+int
+state_put(DB *db, const struct conf *c, const struct dbinfo *dbi)
+{
+ int rv;
+ DBT k, v;
+
+ if (db == NULL)
+ return -1;
+
+ k.data = __UNCONST(c);
+ k.size = sizeof(*c);
+ v.data = __UNCONST(dbi);
+ v.size = sizeof(*dbi);
+
+ switch (rv = (*db->put)(db, &k, &v, 0)) {
+ case 0:
+ if (debug > 1) {
+ (*lfun)(LOG_DEBUG, "%s: returns %d", __func__, rv);
+ (*db->sync)(db, 0);
+ }
+ return 0;
+ case 1:
+ errno = EEXIST;
+ /*FALLTHROUGH*/
+ default:
+ (*lfun)(LOG_ERR, "%s: failed (%m)", __func__);
+ return -1;
+ }
+}
+
+int
+state_iterate(DB *db, struct conf *c, struct dbinfo *dbi, unsigned int first)
+{
+ int rv;
+ DBT k, v;
+
+ if (db == NULL)
+ return -1;
+
+ first = first ? R_FIRST : R_NEXT;
+
+ switch (rv = (*db->seq)(db, &k, &v, first)) {
+ case 0:
+ if (state_sizecheck(&k) == -1)
+ return -1;
+ memcpy(c, k.data, sizeof(*c));
+ if (debug > 2)
+ dumpkey(c);
+ memcpy(dbi, v.data, sizeof(*dbi));
+ if (debug > 1)
+ (*lfun)(LOG_DEBUG, "%s: returns %d", __func__, rv);
+ return 1;
+ case 1:
+ if (debug > 1)
+ (*lfun)(LOG_DEBUG, "%s: returns %d", __func__, rv);
+ return 0;
+ default:
+ (*lfun)(LOG_ERR, "%s: failed (%m)", __func__);
+ return -1;
+ }
+}
+
+int
+state_sync(DB *db)
+{
+ return (*db->sync)(db, 0);
+}
diff --git a/bin/state.h b/bin/state.h
new file mode 100644
index 000000000000..2e9257006e80
--- /dev/null
+++ b/bin/state.h
@@ -0,0 +1,62 @@
+/* $NetBSD: state.h,v 1.5 2015/01/27 19:40:37 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _STATE_H
+#define _STATE_H
+
+#ifdef HAVE_DB_185_H
+#include <db_185.h>
+#elif HAVE_DB_H
+#include <db.h>
+#else
+#error "no db.h"
+#endif
+#include <time.h>
+
+struct dbinfo {
+ int count;
+ time_t last;
+ char id[64];
+};
+
+__BEGIN_DECLS
+struct sockaddr_storage;
+struct conf;
+
+DB *state_open(const char *, int, mode_t);
+int state_close(DB *);
+int state_get(DB *, const struct conf *, struct dbinfo *);
+int state_put(DB *, const struct conf *, const struct dbinfo *);
+int state_del(DB *, const struct conf *);
+int state_iterate(DB *, struct conf *, struct dbinfo *, unsigned int);
+int state_sync(DB *);
+__END_DECLS
+
+#endif /* _STATE_H */
diff --git a/bin/support.c b/bin/support.c
new file mode 100644
index 000000000000..0dac499aca02
--- /dev/null
+++ b/bin/support.c
@@ -0,0 +1,157 @@
+/* $NetBSD: support.c,v 1.8 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: support.c,v 1.8 2016/04/04 15:52:56 christos Exp $");
+
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "support.h"
+
+static __attribute__((__format_arg__(3))) const char *
+expandm(char *buf, size_t len, const char *fmt)
+{
+ char *p;
+ size_t r;
+
+ if ((p = strstr(fmt, "%m")) == NULL)
+ return fmt;
+
+ r = (size_t)(p - fmt);
+ if (r >= len)
+ return fmt;
+
+ strlcpy(buf, fmt, r + 1);
+ strlcat(buf, strerror(errno), len);
+ strlcat(buf, fmt + r + 2, len);
+
+ return buf;
+}
+
+void
+vdlog(int level __unused, const char *fmt, va_list ap)
+{
+ char buf[BUFSIZ];
+
+// fprintf(stderr, "%s: ", getprogname());
+ vfprintf(stderr, expandm(buf, sizeof(buf), fmt), ap);
+ fprintf(stderr, "\n");
+}
+
+void
+dlog(int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vdlog(level, fmt, ap);
+ va_end(ap);
+}
+
+const char *
+fmttime(char *b, size_t l, time_t t)
+{
+ struct tm tm;
+ if (localtime_r(&t, &tm) == NULL)
+ snprintf(b, l, "*%jd*", (intmax_t)t);
+ else
+ strftime(b, l, "%Y/%m/%d %H:%M:%S", &tm);
+ return b;
+}
+
+const char *
+fmtydhms(char *b, size_t l, time_t t)
+{
+ time_t s, m, h, d, y;
+ int z;
+ size_t o;
+
+ s = t % 60;
+ t /= 60;
+ m = t % 60;
+ t /= 60;
+ h = t % 60;
+ t /= 24;
+ d = t % 24;
+ t /= 356;
+ y = t;
+
+ z = 0;
+ o = 0;
+#define APPEND(a) \
+ if (a) { \
+ z = snprintf(b + o, l - o, "%jd%s", (intmax_t)a, __STRING(a)); \
+ if (z == -1) \
+ return b; \
+ o += (size_t)z; \
+ if (o >= l) \
+ return b; \
+ }
+ APPEND(y)
+ APPEND(d)
+ APPEND(h)
+ APPEND(m)
+ APPEND(s)
+ return b;
+}
+
+ssize_t
+blhexdump(char *buf, size_t len, const char *str, const void *b, size_t l)
+{
+ size_t z, cz;
+ int r;
+ const unsigned char *p = b;
+ const unsigned char *e = p + l;
+
+ r = snprintf(buf, len, "%s: ", str);
+ if (r == -1)
+ return -1;
+ if ((cz = z = (size_t)r) >= len)
+ cz = len;
+
+ while (p < e) {
+ r = snprintf(buf + cz, len - cz, "%.2x", *p++);
+ if (r == -1)
+ return -1;
+ if ((cz = (z += (size_t)r)) >= len)
+ cz = len;
+ }
+ return (ssize_t)z;
+}
diff --git a/bin/support.h b/bin/support.h
new file mode 100644
index 000000000000..899649ce8319
--- /dev/null
+++ b/bin/support.h
@@ -0,0 +1,44 @@
+/* $NetBSD: support.h,v 1.7 2016/04/04 15:52:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _SUPPORT_H
+#define _SUPPORT_H
+
+__BEGIN_DECLS
+const char *fmttime(char *, size_t, time_t);
+const char *fmtydhms(char *, size_t, time_t);
+void vdlog(int, const char *, va_list)
+ __attribute__((__format__(__printf__, 2, 0)));
+void dlog(int, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+ssize_t blhexdump(char *, size_t, const char *, const void *, size_t);
+__END_DECLS
+
+#endif /* _SUPPORT_H */
diff --git a/diff/ftpd.diff b/diff/ftpd.diff
new file mode 100644
index 000000000000..d28577f3ef5f
--- /dev/null
+++ b/diff/ftpd.diff
@@ -0,0 +1,91 @@
+--- /dev/null 2015-01-23 17:30:40.000000000 -0500
++++ pfilter.c 2015-01-23 17:12:02.000000000 -0500
+@@ -0,0 +1,24 @@
++#include <stdio.h>
++#include <blacklist.h>
++
++#include "pfilter.h"
++
++static struct blacklist *blstate;
++
++void
++pfilter_open(void)
++{
++ if (blstate == NULL)
++ blstate = blacklist_open();
++}
++
++void
++pfilter_notify(int what, const char *msg)
++{
++ pfilter_open();
++
++ if (blstate == NULL)
++ return;
++
++ blacklist_r(blstate, what, 0, msg);
++}
+--- /dev/null 2015-01-23 17:30:40.000000000 -0500
++++ pfilter.h 2015-01-23 17:07:25.000000000 -0500
+@@ -0,0 +1,2 @@
++void pfilter_open(void);
++void pfilter_notify(int, const char *);
+Index: Makefile
+===================================================================
+RCS file: /cvsroot/src/libexec/ftpd/Makefile,v
+retrieving revision 1.63
+diff -u -p -u -r1.63 Makefile
+--- Makefile 14 Aug 2011 11:46:28 -0000 1.63
++++ Makefile 23 Jan 2015 22:32:20 -0000
+@@ -11,6 +11,10 @@ LDADD+= -lcrypt -lutil
+ MAN= ftpd.conf.5 ftpusers.5 ftpd.8
+ MLINKS= ftpusers.5 ftpchroot.5
+
++SRCS+= pfilter.c
++LDADD+= -lblacklist
++DPADD+= ${LIBBLACKLIST}
++
+ .if defined(NO_INTERNAL_LS)
+ CPPFLAGS+=-DNO_INTERNAL_LS
+ .else
+Index: ftpd.c
+===================================================================
+RCS file: /cvsroot/src/libexec/ftpd/ftpd.c,v
+retrieving revision 1.200
+diff -u -p -u -r1.200 ftpd.c
+--- ftpd.c 31 Jul 2013 19:50:47 -0000 1.200
++++ ftpd.c 23 Jan 2015 22:32:20 -0000
+@@ -165,6 +165,8 @@ __RCSID("$NetBSD: ftpd.c,v 1.200 2013/07
+ #include <security/pam_appl.h>
+ #endif
+
++#include "pfilter.h"
++
+ #define GLOBAL
+ #include "extern.h"
+ #include "pathnames.h"
+@@ -471,6 +473,8 @@ main(int argc, char *argv[])
+ if (EMPTYSTR(confdir))
+ confdir = _DEFAULT_CONFDIR;
+
++ pfilter_open();
++
+ if (dowtmp) {
+ #ifdef SUPPORT_UTMPX
+ ftpd_initwtmpx();
+@@ -1401,6 +1405,7 @@ do_pass(int pass_checked, int pass_rval,
+ if (rval) {
+ reply(530, "%s", rval == 2 ? "Password expired." :
+ "Login incorrect.");
++ pfilter_notify(1, rval == 2 ? "exppass" : "badpass");
+ if (logging) {
+ syslog(LOG_NOTICE,
+ "FTP LOGIN FAILED FROM %s", remoteloghost);
+@@ -1444,6 +1449,7 @@ do_pass(int pass_checked, int pass_rval,
+ *remote_ip = 0;
+ remote_ip[sizeof(remote_ip) - 1] = 0;
+ if (!auth_hostok(lc, remotehost, remote_ip)) {
++ pfilter_notify(1, "bannedhost");
+ syslog(LOG_INFO|LOG_AUTH,
+ "FTP LOGIN FAILED (HOST) as %s: permission denied.",
+ pw->pw_name);
diff --git a/diff/named.diff b/diff/named.diff
new file mode 100644
index 000000000000..fcd97ba7ec1f
--- /dev/null
+++ b/diff/named.diff
@@ -0,0 +1,216 @@
+--- /dev/null 2015-01-22 01:48:00.000000000 -0500
++++ dist/bin/named/pfilter.c 2015-01-22 01:35:16.000000000 -0500
+@@ -0,0 +1,42 @@
++#include <config.h>
++
++#include <isc/platform.h>
++#include <isc/util.h>
++#include <named/types.h>
++#include <named/client.h>
++
++#include <blacklist.h>
++
++#include "pfilter.h"
++
++static struct blacklist *blstate;
++
++void
++pfilter_open(void)
++{
++ if (blstate == NULL)
++ blstate = blacklist_open();
++}
++
++#define TCP_CLIENT(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
++
++void
++pfilter_notify(isc_result_t res, ns_client_t *client, const char *msg)
++{
++ isc_socket_t *socket;
++
++ pfilter_open();
++
++ if (TCP_CLIENT(client))
++ socket = client->tcpsocket;
++ else {
++ socket = client->udpsocket;
++ if (!client->peeraddr_valid)
++ return;
++ }
++ if (socket == NULL)
++ return;
++ blacklist_sa_r(blstate,
++ res != ISC_R_SUCCESS, isc_socket_getfd(socket),
++ &client->peeraddr.type.sa, client->peeraddr.length, msg);
++}
+--- /dev/null 2015-01-22 01:48:00.000000000 -0500
++++ dist/bin/named/pfilter.h 2015-01-22 01:16:56.000000000 -0500
+@@ -0,0 +1,2 @@
++void pfilter_open(void);
++void pfilter_notify(isc_result_t, ns_client_t *, const char *);
+Index: bin/named/Makefile
+===================================================================
+RCS file: /cvsroot/src/external/bsd/bind/bin/named/Makefile,v
+retrieving revision 1.8
+diff -u -u -r1.8 Makefile
+--- bin/named/Makefile 31 Dec 2013 20:23:12 -0000 1.8
++++ bin/named/Makefile 23 Jan 2015 21:37:09 -0000
+@@ -33,7 +33,9 @@
+ lwaddr.c lwdclient.c lwderror.c \
+ lwdgabn.c lwdgnba.c lwdgrbn.c lwdnoop.c lwresd.c lwsearch.c \
+ main.c notify.c query.c server.c sortlist.c statschannel.c \
+- tkeyconf.c tsigconf.c \
++ pfilter.c tkeyconf.c tsigconf.c \
+ update.c xfrout.c zoneconf.c ${SRCS_UNIX}
+
++LDADD+=-lblacklist
++DPADD+=${LIBBLACKLIST}
+ .include <bsd.prog.mk>
+Index: dist/bin/named/client.c
+===================================================================
+RCS file: /cvsroot/src/external/bsd/bind/dist/bin/named/client.c,v
+retrieving revision 1.11
+diff -u -u -r1.11 client.c
+--- dist/bin/named/client.c 10 Dec 2014 04:37:51 -0000 1.11
++++ dist/bin/named/client.c 23 Jan 2015 21:37:09 -0000
+@@ -65,6 +65,8 @@
+ #include <named/server.h>
+ #include <named/update.h>
+
++#include "pfilter.h"
++
+ /***
+ *** Client
+ ***/
+@@ -3101,6 +3103,7 @@
+ result = ns_client_checkaclsilent(client, sockaddr ? &netaddr : NULL,
+ acl, default_allow);
+
++ pfilter_notify(result, client, opname);
+ if (result == ISC_R_SUCCESS)
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+Index: dist/bin/named/main.c
+===================================================================
+RCS file: /cvsroot/src/external/bsd/bind/dist/bin/named/main.c,v
+retrieving revision 1.15
+diff -u -u -r1.15 main.c
+--- dist/bin/named/main.c 10 Dec 2014 04:37:51 -0000 1.15
++++ dist/bin/named/main.c 23 Jan 2015 21:37:09 -0000
+@@ -83,6 +83,9 @@
+ #ifdef HAVE_LIBXML2
+ #include <libxml/xmlversion.h>
+ #endif
++
++#include "pfilter.h"
++
+ /*
+ * Include header files for database drivers here.
+ */
+@@ -1206,6 +1209,8 @@
+
+ parse_command_line(argc, argv);
+
++ pfilter_open();
++
+ /*
+ * Warn about common configuration error.
+ */
+Index: dist/bin/named/query.c
+===================================================================
+RCS file: /cvsroot/src/external/bsd/bind/dist/bin/named/query.c,v
+retrieving revision 1.17
+diff -u -u -r1.17 query.c
+--- dist/bin/named/query.c 10 Dec 2014 04:37:52 -0000 1.17
++++ dist/bin/named/query.c 23 Jan 2015 21:37:09 -0000
+@@ -65,6 +65,8 @@
+ #include <named/sortlist.h>
+ #include <named/xfrout.h>
+
++#include "pfilter.h"
++
+ #if 0
+ /*
+ * It has been recommended that DNS64 be changed to return excluded
+@@ -762,6 +764,8 @@
+ }
+
+ result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE);
++ if (result != ISC_R_SUCCESS)
++ pfilter_notify(result, client, "validatezonedb");
+ if ((options & DNS_GETDB_NOLOG) == 0) {
+ char msg[NS_CLIENT_ACLMSGSIZE("query")];
+ if (result == ISC_R_SUCCESS) {
+@@ -1026,6 +1030,8 @@
+ result = ns_client_checkaclsilent(client, NULL,
+ client->view->cacheacl,
+ ISC_TRUE);
++ if (result == ISC_R_SUCCESS)
++ pfilter_notify(result, client, "cachedb");
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We were allowed by the "allow-query-cache" ACL.
+Index: dist/bin/named/update.c
+===================================================================
+RCS file: /cvsroot/src/external/bsd/bind/dist/bin/named/update.c,v
+retrieving revision 1.9
+diff -u -u -r1.9 update.c
+--- dist/bin/named/update.c 10 Dec 2014 04:37:52 -0000 1.9
++++ dist/bin/named/update.c 23 Jan 2015 21:37:09 -0000
+@@ -59,6 +59,8 @@
+ #include <named/server.h>
+ #include <named/update.h>
+
++#include "pfilter.h"
++
+ /*! \file
+ * \brief
+ * This module implements dynamic update as in RFC2136.
+@@ -307,6 +309,7 @@
+
+ result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
++ pfilter_notify(result, client, "queryacl");
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(client->view->rdclass, classbuf,
+ sizeof(classbuf));
+@@ -324,6 +327,7 @@
+ sizeof(classbuf));
+
+ result = DNS_R_REFUSED;
++ pfilter_notify(result, client, "updateacl");
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
+ NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
+ "update '%s/%s' denied", namebuf, classbuf);
+@@ -362,6 +366,7 @@
+ msg = "disabled";
+ } else {
+ result = ns_client_checkaclsilent(client, NULL, acl, ISC_FALSE);
++ pfilter_notify(result, client, "updateacl");
+ if (result == ISC_R_SUCCESS) {
+ level = ISC_LOG_DEBUG(3);
+ msg = "approved";
+Index: dist/bin/named/xfrout.c
+===================================================================
+RCS file: /cvsroot/src/external/bsd/bind/dist/bin/named/xfrout.c,v
+retrieving revision 1.7
+diff -u -u -r1.7 xfrout.c
+--- dist/bin/named/xfrout.c 10 Dec 2014 04:37:52 -0000 1.7
++++ dist/bin/named/xfrout.c 23 Jan 2015 21:37:09 -0000
+@@ -54,6 +54,8 @@
+ #include <named/server.h>
+ #include <named/xfrout.h>
+
++#include "pfilter.h"
++
+ /*! \file
+ * \brief
+ * Outgoing AXFR and IXFR.
+@@ -822,6 +824,7 @@
+ &client->peeraddr,
+ &db);
+
++ pfilter_notify(result, client, "zonexfr");
+ if (result == ISC_R_NOPERM) {
+ char _buf1[DNS_NAME_FORMATSIZE];
+ char _buf2[DNS_RDATACLASS_FORMATSIZE];
diff --git a/diff/proftpd.diff b/diff/proftpd.diff
new file mode 100644
index 000000000000..c811c9cf50bc
--- /dev/null
+++ b/diff/proftpd.diff
@@ -0,0 +1,124 @@
+--- Make.rules.in.orig 2015-05-27 20:25:54.000000000 -0400
++++ Make.rules.in 2016-01-25 21:48:47.000000000 -0500
+@@ -110,3 +110,8 @@
+
+ FTPWHO_OBJS=ftpwho.o scoreboard.o misc.o
+ BUILD_FTPWHO_OBJS=utils/ftpwho.o utils/scoreboard.o utils/misc.o
++
++CPPFLAGS+=-DHAVE_BLACKLIST
++LIBS+=-lblacklist
++OBJS+= pfilter.o
++BUILD_OBJS+= src/pfilter.o
+--- /dev/null 2016-01-22 17:30:55.000000000 -0500
++++ include/pfilter.h 2016-01-22 16:18:33.000000000 -0500
+@@ -0,0 +1,3 @@
++
++void pfilter_notify(int);
++void pfilter_init(void);
+--- modules/mod_auth.c.orig 2015-05-27 20:25:54.000000000 -0400
++++ modules/mod_auth.c 2016-01-22 16:21:06.000000000 -0500
+@@ -30,6 +30,7 @@
+
+ #include "conf.h"
+ #include "privs.h"
++#include "pfilter.h"
+
+ extern pid_t mpid;
+
+@@ -84,6 +85,8 @@
+ _("Login timeout (%d %s): closing control connection"), TimeoutLogin,
+ TimeoutLogin != 1 ? "seconds" : "second");
+
++ pfilter_notify(1);
++
+ /* It's possible that any listeners of this event might terminate the
+ * session process themselves (e.g. mod_ban). So write out that the
+ * TimeoutLogin has been exceeded to the log here, in addition to the
+@@ -913,6 +916,7 @@
+ pr_memscrub(pass, strlen(pass));
+ }
+
++ pfilter_notify(1);
+ pr_log_auth(PR_LOG_NOTICE, "SECURITY VIOLATION: Root login attempted");
+ return 0;
+ }
+@@ -1726,6 +1730,7 @@
+ return 1;
+
+ auth_failure:
++ pfilter_notify(1);
+ if (pass)
+ pr_memscrub(pass, strlen(pass));
+ session.user = session.group = NULL;
+--- src/main.c.orig 2016-01-22 17:36:43.000000000 -0500
++++ src/main.c 2016-01-22 17:37:58.000000000 -0500
+@@ -49,6 +49,7 @@
+ #endif
+
+ #include "privs.h"
++#include "pfilter.h"
+
+ int (*cmd_auth_chk)(cmd_rec *);
+ void (*cmd_handler)(server_rec *, conn_t *);
+@@ -1050,6 +1051,7 @@
+ pid_t pid;
+ sigset_t sig_set;
+
++ pfilter_init();
+ if (!nofork) {
+
+ /* A race condition exists on heavily loaded servers where the parent
+@@ -1169,7 +1171,8 @@
+
+ /* Reseed pseudo-randoms */
+ srand((unsigned int) (time(NULL) * getpid()));
+-
++#else
++ pfilter_init();
+ #endif /* PR_DEVEL_NO_FORK */
+
+ /* Child is running here */
+--- /dev/null 2016-01-22 17:30:55.000000000 -0500
++++ src/pfilter.c 2016-01-22 16:37:55.000000000 -0500
+@@ -0,0 +1,41 @@
++#include "pfilter.h"
++#include "conf.h"
++#include "privs.h"
++#ifdef HAVE_BLACKLIST
++#include <blacklist.h>
++#endif
++
++static struct blacklist *blstate;
++
++void
++pfilter_init(void)
++{
++#ifdef HAVE_BLACKLIST
++ if (blstate == NULL)
++ blstate = blacklist_open();
++#endif
++}
++
++void
++pfilter_notify(int a)
++{
++#ifdef HAVE_BLACKLIST
++ conn_t *c = session.c;
++ int fd;
++
++ if (c == NULL)
++ return;
++ if (c->rfd != -1)
++ fd = c->rfd;
++ else if (c->wfd != -1)
++ fd = c->wfd;
++ else
++ return;
++
++ if (blstate == NULL)
++ pfilter_init();
++ if (blstate == NULL)
++ return;
++ (void)blacklist_r(blstate, a, fd, "proftpd");
++#endif
++}
diff --git a/diff/ssh.diff b/diff/ssh.diff
new file mode 100644
index 000000000000..bc0b75c05674
--- /dev/null
+++ b/diff/ssh.diff
@@ -0,0 +1,231 @@
+--- /dev/null 2015-01-22 23:10:33.000000000 -0500
++++ dist/pfilter.c 2015-01-22 23:46:03.000000000 -0500
+@@ -0,0 +1,28 @@
++#include "namespace.h"
++#include "includes.h"
++#include "ssh.h"
++#include "packet.h"
++#include "log.h"
++#include "pfilter.h"
++#include <blacklist.h>
++
++static struct blacklist *blstate;
++
++void
++pfilter_init(void)
++{
++ blstate = blacklist_open();
++}
++
++void
++pfilter_notify(int a)
++{
++ int fd;
++ if (blstate == NULL)
++ pfilter_init();
++ if (blstate == NULL)
++ return;
++ // XXX: 3?
++ fd = packet_connection_is_on_socket() ? packet_get_connection_in() : 3;
++ (void)blacklist_r(blstate, a, fd, "ssh");
++}
+--- /dev/null 2015-01-20 21:14:44.000000000 -0500
++++ dist/pfilter.h 2015-01-20 20:16:20.000000000 -0500
+@@ -0,0 +1,3 @@
++
++void pfilter_notify(int);
++void pfilter_init(void);
+Index: bin/sshd/Makefile
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/bin/sshd/Makefile,v
+retrieving revision 1.10
+diff -u -u -r1.10 Makefile
+--- bin/sshd/Makefile 19 Oct 2014 16:30:58 -0000 1.10
++++ bin/sshd/Makefile 22 Jan 2015 21:39:21 -0000
+@@ -15,7 +15,7 @@
+ auth2-none.c auth2-passwd.c auth2-pubkey.c \
+ monitor_mm.c monitor.c monitor_wrap.c \
+ kexdhs.c kexgexs.c kexecdhs.c sftp-server.c sftp-common.c \
+- roaming_common.c roaming_serv.c sandbox-rlimit.c
++ roaming_common.c roaming_serv.c sandbox-rlimit.c pfilter.c
+
+ COPTS.auth-options.c= -Wno-pointer-sign
+ COPTS.ldapauth.c= -Wno-format-nonliteral # XXX: should fix
+@@ -68,3 +68,6 @@
+
+ LDADD+= -lwrap
+ DPADD+= ${LIBWRAP}
++
++LDADD+= -lblacklist
++DPADD+= ${LIBBLACKLIST}
+Index: dist/auth.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/auth.c,v
+retrieving revision 1.10
+diff -u -u -r1.10 auth.c
+--- dist/auth.c 19 Oct 2014 16:30:58 -0000 1.10
++++ dist/auth.c 22 Jan 2015 21:39:22 -0000
+@@ -62,6 +62,7 @@
+ #include "monitor_wrap.h"
+ #include "krl.h"
+ #include "compat.h"
++#include "pfilter.h"
+
+ #ifdef HAVE_LOGIN_CAP
+ #include <login_cap.h>
+@@ -362,6 +363,8 @@
+ compat20 ? "ssh2" : "ssh1",
+ authctxt->info != NULL ? ": " : "",
+ authctxt->info != NULL ? authctxt->info : "");
++ if (!authctxt->postponed)
++ pfilter_notify(!authenticated);
+ free(authctxt->info);
+ authctxt->info = NULL;
+ }
+Index: dist/sshd.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/sshd.c,v
+retrieving revision 1.15
+diff -u -u -r1.15 sshd.c
+--- dist/sshd.c 28 Oct 2014 21:36:16 -0000 1.15
++++ dist/sshd.c 22 Jan 2015 21:39:22 -0000
+@@ -109,6 +109,7 @@
+ #include "roaming.h"
+ #include "ssh-sandbox.h"
+ #include "version.h"
++#include "pfilter.h"
+
+ #ifdef LIBWRAP
+ #include <tcpd.h>
+@@ -364,6 +365,7 @@
+ killpg(0, SIGTERM);
+ }
+
++ pfilter_notify(1);
+ /* Log error and exit. */
+ sigdie("Timeout before authentication for %s", get_remote_ipaddr());
+ }
+@@ -1160,6 +1162,7 @@
+ for (i = 0; i < options.max_startups; i++)
+ startup_pipes[i] = -1;
+
++ pfilter_init();
+ /*
+ * Stay listening for connections until the system crashes or
+ * the daemon is killed with a signal.
+Index: auth1.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/auth1.c,v
+retrieving revision 1.9
+diff -u -u -r1.9 auth1.c
+--- auth1.c 19 Oct 2014 16:30:58 -0000 1.9
++++ auth1.c 14 Feb 2015 15:40:51 -0000
+@@ -41,6 +41,7 @@
+ #endif
+ #include "monitor_wrap.h"
+ #include "buffer.h"
++#include "pfilter.h"
+
+ /* import */
+ extern ServerOptions options;
+@@ -445,6 +446,7 @@
+ else {
+ debug("do_authentication: invalid user %s", user);
+ authctxt->pw = fakepw();
++ pfilter_notify(1);
+ }
+
+ /* Configuration may have changed as a result of Match */
+Index: auth2.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/auth2.c,v
+retrieving revision 1.9
+diff -u -u -r1.9 auth2.c
+--- auth2.c 19 Oct 2014 16:30:58 -0000 1.9
++++ auth2.c 14 Feb 2015 15:40:51 -0000
+@@ -52,6 +52,7 @@
+ #include "pathnames.h"
+ #include "buffer.h"
+ #include "canohost.h"
++#include "pfilter.h"
+
+ #ifdef GSSAPI
+ #include "ssh-gss.h"
+@@ -256,6 +257,7 @@
+ } else {
+ logit("input_userauth_request: invalid user %s", user);
+ authctxt->pw = fakepw();
++ pfilter_notify(1);
+ }
+ #ifdef USE_PAM
+ if (options.use_pam)
+Index: sshd.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/sshd.c,v
+retrieving revision 1.16
+diff -u -r1.16 sshd.c
+--- sshd.c 25 Jan 2015 15:52:44 -0000 1.16
++++ sshd.c 14 Feb 2015 09:55:06 -0000
+@@ -628,6 +628,8 @@
+ explicit_bzero(pw->pw_passwd, strlen(pw->pw_passwd));
+ endpwent();
+
++ pfilter_init();
++
+ /* Change our root directory */
+ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1)
+ fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR,
+
+Index: auth-pam.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/auth-pam.c,v
+retrieving revision 1.7
+diff -u -u -r1.7 auth-pam.c
+--- auth-pam.c 3 Jul 2015 00:59:59 -0000 1.7
++++ auth-pam.c 23 Jan 2016 00:01:16 -0000
+@@ -114,6 +114,7 @@
+ #include "ssh-gss.h"
+ #endif
+ #include "monitor_wrap.h"
++#include "pfilter.h"
+
+ extern ServerOptions options;
+ extern Buffer loginmsg;
+@@ -809,6 +810,7 @@
+ free(msg);
+ return (0);
+ }
++ pfilter_notify(1);
+ error("PAM: %s for %s%.100s from %.100s", msg,
+ sshpam_authctxt->valid ? "" : "illegal user ",
+ sshpam_authctxt->user,
+Index: auth.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/auth.c,v
+retrieving revision 1.15
+diff -u -u -r1.15 auth.c
+--- auth.c 21 Aug 2015 08:20:59 -0000 1.15
++++ auth.c 23 Jan 2016 00:01:16 -0000
+@@ -656,6 +656,7 @@
+
+ pw = getpwnam(user);
+ if (pw == NULL) {
++ pfilter_notify(1);
+ logit("Invalid user %.100s from %.100s",
+ user, get_remote_ipaddr());
+ return (NULL);
+Index: auth1.c
+===================================================================
+RCS file: /cvsroot/src/crypto/external/bsd/openssh/dist/auth1.c,v
+retrieving revision 1.12
+diff -u -u -r1.12 auth1.c
+--- auth1.c 3 Jul 2015 00:59:59 -0000 1.12
++++ auth1.c 23 Jan 2016 00:01:16 -0000
+@@ -376,6 +376,7 @@
+ char *msg;
+ size_t len;
+
++ pfilter_notify(1);
+ error("Access denied for user %s by PAM account "
+ "configuration", authctxt->user);
+ len = buffer_len(&loginmsg);
diff --git a/etc/Makefile b/etc/Makefile
new file mode 100644
index 000000000000..669528ddca89
--- /dev/null
+++ b/etc/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.3 2015/01/26 00:18:40 christos Exp $
+
+SUBDIR=rc.d
+
+FILESDIR= /usr/share/examples/blacklist
+FILESMODE= 644
+FILES= blacklistd.conf npf.conf
+
+.include <bsd.files.mk>
+.include <bsd.subdir.mk>
diff --git a/etc/blacklistd.conf b/etc/blacklistd.conf
new file mode 100644
index 000000000000..f061b004ad36
--- /dev/null
+++ b/etc/blacklistd.conf
@@ -0,0 +1,14 @@
+# Blacklist rule
+# adr/mask:port type proto owner name nfail disable
+[local]
+ssh stream * * * 3 6h
+ftp stream * * * 3 6h
+domain * * named * 3 12h
+#6161 stream tcp6 christos * 2 10m
+* * * * * 3 60
+
+# adr/mask:port type proto owner name nfail disable
+[remote]
+#129.168.0.0/16 * * * = * *
+#6161 = = = =/24 = =
+#* stream tcp * = = =
diff --git a/etc/npf.conf b/etc/npf.conf
new file mode 100644
index 000000000000..42d56044ad6e
--- /dev/null
+++ b/etc/npf.conf
@@ -0,0 +1,15 @@
+# Transparent firewall example for blacklistd
+
+$ext_if = "bnx0"
+
+set bpf.jit on;
+alg "icmp"
+
+group "external" on $ext_if {
+ ruleset "blacklistd"
+ pass final all
+}
+
+group default {
+ pass final all
+}
diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile
new file mode 100644
index 000000000000..e863d0853a0f
--- /dev/null
+++ b/etc/rc.d/Makefile
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile,v 1.1 2015/01/22 17:49:41 christos Exp $
+
+SCRIPTS=blacklistd
+SCRIPTSDIR=/etc/rc.d
+
+.include <bsd.prog.mk>
diff --git a/etc/rc.d/blacklistd b/etc/rc.d/blacklistd
new file mode 100644
index 000000000000..2e46f518fc70
--- /dev/null
+++ b/etc/rc.d/blacklistd
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# $NetBSD: blacklistd,v 1.1 2015/01/22 17:49:41 christos Exp $
+#
+
+# PROVIDE: blacklistd
+# REQUIRE: npf
+# BEFORE: SERVERS
+
+$_rc_subr_loaded . /etc/rc.subr
+
+name="blacklistd"
+rcvar=$name
+command="/sbin/${name}"
+pidfile="/var/run/${name}.pid"
+required_files="/etc/${name}.conf"
+start_precmd="${name}_precmd"
+extra_commands="reload"
+
+_sockfile="/var/run/${name}.sockets"
+_sockname="blsock"
+
+blacklistd_precmd()
+{
+ # Create default list of blacklistd sockets to watch
+ #
+ ( umask 022 ; > $_sockfile )
+
+ # Find /etc/rc.d scripts with "chrootdir" rcorder(8) keyword,
+ # and if $${app}_chrootdir is a directory, add appropriate
+ # blacklistd socket to list of sockets to watch.
+ #
+ for _lr in $(rcorder -k chrootdir /etc/rc.d/*); do
+ (
+ _l=${_lr##*/}
+ load_rc_config ${_l}
+ eval _ldir=\$${_l}_chrootdir
+ if checkyesno $_l && [ -n "$_ldir" ]; then
+ echo "${_ldir}/var/run/${_sockname}" >> $_sockfile
+ fi
+ )
+ done
+
+ # If other sockets have been provided, change run_rc_command()'s
+ # internal copy of $blacklistd_flags to force use of specific
+ # blacklistd sockets.
+ #
+ if [ -s $_sockfile ]; then
+ echo "/var/run/${_sockname}" >> $_sockfile
+ rc_flags="-P $_sockfile $rc_flags"
+ fi
+
+ return 0
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/include/Makefile b/include/Makefile
new file mode 100644
index 000000000000..6854907be25e
--- /dev/null
+++ b/include/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2015/01/21 16:16:00 christos Exp $
+
+# Doing a make includes builds /usr/include
+
+NOOBJ= # defined
+
+INCS= blacklist.h
+INCSDIR= /usr/include
+
+.include <bsd.prog.mk>
diff --git a/include/bl.h b/include/bl.h
new file mode 100644
index 000000000000..68249cdf8230
--- /dev/null
+++ b/include/bl.h
@@ -0,0 +1,76 @@
+/* $NetBSD: bl.h,v 1.13 2016/03/11 17:16:40 christos Exp $ */
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _BL_H
+#define _BL_H
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include "blacklist.h"
+
+typedef enum {
+ BL_INVALID,
+ BL_ADD,
+ BL_DELETE
+} bl_type_t;
+
+typedef struct {
+ bl_type_t bi_type;
+ int bi_fd;
+ uid_t bi_uid;
+ gid_t bi_gid;
+ socklen_t bi_slen;
+ struct sockaddr_storage bi_ss;
+ char bi_msg[1024];
+} bl_info_t;
+
+#define bi_cred bi_u._bi_cred
+
+#ifndef _PATH_BLSOCK
+#define _PATH_BLSOCK "/var/run/blacklistd.sock"
+#endif
+
+__BEGIN_DECLS
+
+typedef struct blacklist *bl_t;
+
+bl_t bl_create(bool, const char *, void (*)(int, const char *, va_list));
+void bl_destroy(bl_t);
+int bl_send(bl_t, bl_type_t, int, const struct sockaddr *, socklen_t,
+ const char *);
+int bl_getfd(bl_t);
+bl_info_t *bl_recv(bl_t);
+bool bl_isconnected(bl_t);
+
+__END_DECLS
+
+#endif /* _BL_H */
diff --git a/include/blacklist.h b/include/blacklist.h
new file mode 100644
index 000000000000..9ebe11b95e08
--- /dev/null
+++ b/include/blacklist.h
@@ -0,0 +1,46 @@
+/* $NetBSD: blacklist.h,v 1.3 2015/01/23 18:48:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 _BLACKLIST_H
+#define _BLACKLIST_H
+
+#include <sys/socket.h>
+
+__BEGIN_DECLS
+struct blacklist *blacklist_open(void);
+void blacklist_close(struct blacklist *);
+int blacklist(int, int, const char *);
+int blacklist_r(struct blacklist *, int, int, const char *);
+int blacklist_sa(int, int, const struct sockaddr *, socklen_t, const char *);
+int blacklist_sa_r(struct blacklist *, int, int,
+ const struct sockaddr *, socklen_t, const char *);
+__END_DECLS
+
+#endif /* _BLACKLIST_H */
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 000000000000..aea01caca228
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,19 @@
+# $NetBSD: Makefile,v 1.6 2016/01/05 13:07:46 christos Exp $
+
+.include <bsd.own.mk>
+
+USE_SHLIBDIR= yes
+
+CPPFLAGS+=-D_REENTRANT
+LIBDPLIBS+=pthread ${NETBSDSRCDIR}/lib/libpthread
+LIB=blacklist
+SRCS=bl.c blacklist.c
+MAN=libblacklist.3
+MLINKS+=libblacklist.3 blacklist_open.3
+MLINKS+=libblacklist.3 blacklist_close.3
+MLINKS+=libblacklist.3 blacklist.3
+MLINKS+=libblacklist.3 blacklist_r.3
+MLINKS+=libblacklist.3 blacklist_sa.3
+MLINKS+=libblacklist.3 blacklist_sa_r.3
+
+.include <bsd.lib.mk>
diff --git a/lib/bl.c b/lib/bl.c
new file mode 100644
index 000000000000..bca52cfbc8fe
--- /dev/null
+++ b/lib/bl.c
@@ -0,0 +1,524 @@
+/* $NetBSD: bl.c,v 1.27 2015/12/30 16:42:48 christos Exp $ */
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: bl.c,v 1.27 2015/12/30 16:42:48 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#ifdef _REENTRANT
+#include <pthread.h>
+#endif
+
+#include "bl.h"
+
+typedef struct {
+ uint32_t bl_len;
+ uint32_t bl_version;
+ uint32_t bl_type;
+ uint32_t bl_salen;
+ struct sockaddr_storage bl_ss;
+ char bl_data[];
+} bl_message_t;
+
+struct blacklist {
+#ifdef _REENTRANT
+ pthread_mutex_t b_mutex;
+# define BL_INIT(b) pthread_mutex_init(&b->b_mutex, NULL)
+# define BL_LOCK(b) pthread_mutex_lock(&b->b_mutex)
+# define BL_UNLOCK(b) pthread_mutex_unlock(&b->b_mutex)
+#else
+# define BL_INIT(b) do {} while(/*CONSTCOND*/0)
+# define BL_LOCK(b) BL_INIT(b)
+# define BL_UNLOCK(b) BL_INIT(b)
+#endif
+ int b_fd;
+ int b_connected;
+ struct sockaddr_un b_sun;
+ void (*b_fun)(int, const char *, va_list);
+ bl_info_t b_info;
+};
+
+#define BL_VERSION 1
+
+bool
+bl_isconnected(bl_t b)
+{
+ return b->b_connected == 0;
+}
+
+int
+bl_getfd(bl_t b)
+{
+ return b->b_fd;
+}
+
+static void
+bl_reset(bl_t b, bool locked)
+{
+ int serrno = errno;
+ if (!locked)
+ BL_LOCK(b);
+ close(b->b_fd);
+ errno = serrno;
+ b->b_fd = -1;
+ b->b_connected = -1;
+ if (!locked)
+ BL_UNLOCK(b);
+}
+
+static void
+bl_log(void (*fun)(int, const char *, va_list), int level,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int serrno = errno;
+
+ va_start(ap, fmt);
+ (*fun)(level, fmt, ap);
+ va_end(ap);
+ errno = serrno;
+}
+
+static int
+bl_init(bl_t b, bool srv)
+{
+ static int one = 1;
+ /* AF_UNIX address of local logger */
+ mode_t om;
+ int rv, serrno;
+ struct sockaddr_un *sun = &b->b_sun;
+
+#ifndef SOCK_NONBLOCK
+#define SOCK_NONBLOCK 0
+#endif
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+#ifndef SOCK_NOSIGPIPE
+#define SOCK_NOSIGPIPE 0
+#endif
+
+ BL_LOCK(b);
+
+ if (b->b_fd == -1) {
+ b->b_fd = socket(PF_LOCAL,
+ SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0);
+ if (b->b_fd == -1) {
+ bl_log(b->b_fun, LOG_ERR, "%s: socket failed (%m)",
+ __func__);
+ BL_UNLOCK(b);
+ return -1;
+ }
+#if SOCK_CLOEXEC == 0
+ fcntl(b->b_fd, F_SETFD, FD_CLOEXEC);
+#endif
+#if SOCK_NONBLOCK == 0
+ fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK);
+#endif
+#if SOCK_NOSIGPIPE == 0
+#ifdef SO_NOSIGPIPE
+ int o = 1;
+ setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o));
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif
+#endif
+ }
+
+ if (bl_isconnected(b)) {
+ BL_UNLOCK(b);
+ return 0;
+ }
+
+ /*
+ * We try to connect anyway even when we are a server to verify
+ * that no other server is listening to the socket. If we succeed
+ * to connect and we are a server, someone else owns it.
+ */
+ rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
+ if (rv == 0) {
+ if (srv) {
+ bl_log(b->b_fun, LOG_ERR,
+ "%s: another daemon is handling `%s'",
+ __func__, sun->sun_path);
+ goto out;
+ }
+ } else {
+ if (!srv) {
+ /*
+ * If the daemon is not running, we just try a
+ * connect, so leave the socket alone until it does
+ * and only log once.
+ */
+ if (b->b_connected != 1) {
+ bl_log(b->b_fun, LOG_DEBUG,
+ "%s: connect failed for `%s' (%m)",
+ __func__, sun->sun_path);
+ b->b_connected = 1;
+ }
+ BL_UNLOCK(b);
+ return -1;
+ }
+ bl_log(b->b_fun, LOG_DEBUG, "Connected to blacklist server",
+ __func__);
+ }
+
+ if (srv) {
+ (void)unlink(sun->sun_path);
+ om = umask(0);
+ rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
+ serrno = errno;
+ (void)umask(om);
+ errno = serrno;
+ if (rv == -1) {
+ bl_log(b->b_fun, LOG_ERR,
+ "%s: bind failed for `%s' (%m)",
+ __func__, sun->sun_path);
+ goto out;
+ }
+ }
+
+ b->b_connected = 0;
+#define GOT_FD 1
+#if defined(LOCAL_CREDS)
+#define CRED_LEVEL 0
+#define CRED_NAME LOCAL_CREDS
+#define CRED_SC_UID sc_euid
+#define CRED_SC_GID sc_egid
+#define CRED_MESSAGE SCM_CREDS
+#define CRED_SIZE SOCKCREDSIZE(NGROUPS_MAX)
+#define CRED_TYPE struct sockcred
+#define GOT_CRED 2
+#elif defined(SO_PASSCRED)
+#define CRED_LEVEL SOL_SOCKET
+#define CRED_NAME SO_PASSCRED
+#define CRED_SC_UID uid
+#define CRED_SC_GID gid
+#define CRED_MESSAGE SCM_CREDENTIALS
+#define CRED_SIZE sizeof(struct ucred)
+#define CRED_TYPE struct ucred
+#define GOT_CRED 2
+#else
+#define GOT_CRED 0
+/*
+ * getpeereid() and LOCAL_PEERCRED don't help here
+ * because we are not a stream socket!
+ */
+#define CRED_SIZE 0
+#define CRED_TYPE void * __unused
+#endif
+
+#ifdef CRED_LEVEL
+ if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME,
+ &one, (socklen_t)sizeof(one)) == -1) {
+ bl_log(b->b_fun, LOG_ERR, "%s: setsockopt %s "
+ "failed (%m)", __func__, __STRING(CRED_NAME));
+ goto out;
+ }
+#endif
+
+ BL_UNLOCK(b);
+ return 0;
+out:
+ bl_reset(b, true);
+ BL_UNLOCK(b);
+ return -1;
+}
+
+bl_t
+bl_create(bool srv, const char *path, void (*fun)(int, const char *, va_list))
+{
+ bl_t b = calloc(1, sizeof(*b));
+ if (b == NULL)
+ goto out;
+ b->b_fun = fun == NULL ? vsyslog : fun;
+ b->b_fd = -1;
+ b->b_connected = -1;
+ BL_INIT(b);
+
+ memset(&b->b_sun, 0, sizeof(b->b_sun));
+ b->b_sun.sun_family = AF_LOCAL;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ b->b_sun.sun_len = sizeof(b->b_sun);
+#endif
+ strlcpy(b->b_sun.sun_path,
+ path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path));
+
+ bl_init(b, srv);
+ return b;
+out:
+ free(b);
+ bl_log(fun, LOG_ERR, "%s: malloc failed (%m)", __func__);
+ return NULL;
+}
+
+void
+bl_destroy(bl_t b)
+{
+ bl_reset(b, false);
+ free(b);
+}
+
+static int
+bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa,
+ socklen_t slen, const char *ctx)
+{
+ uint8_t family;
+
+ memset(ss, 0, sizeof(*ss));
+
+ switch (slen) {
+ case 0:
+ return 0;
+ case sizeof(struct sockaddr_in):
+ family = AF_INET;
+ break;
+ case sizeof(struct sockaddr_in6):
+ family = AF_INET6;
+ break;
+ default:
+ bl_log(b->b_fun, LOG_ERR, "%s: invalid socket len %u (%s)",
+ __func__, (unsigned)slen, ctx);
+ errno = EINVAL;
+ return -1;
+ }
+
+ memcpy(ss, sa, slen);
+
+ if (ss->ss_family != family) {
+ bl_log(b->b_fun, LOG_INFO,
+ "%s: correcting socket family %d to %d (%s)",
+ __func__, ss->ss_family, family, ctx);
+ ss->ss_family = family;
+ }
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ if (ss->ss_len != slen) {
+ bl_log(b->b_fun, LOG_INFO,
+ "%s: correcting socket len %u to %u (%s)",
+ __func__, ss->ss_len, (unsigned)slen, ctx);
+ ss->ss_len = (uint8_t)slen;
+ }
+#endif
+ return 0;
+}
+
+int
+bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa,
+ socklen_t slen, const char *ctx)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ char ctrl[CMSG_SPACE(sizeof(int))];
+ uint32_t fd;
+ } ua;
+ struct cmsghdr *cmsg;
+ union {
+ bl_message_t bl;
+ char buf[512];
+ } ub;
+ size_t ctxlen, tried;
+#define NTRIES 5
+
+ ctxlen = strlen(ctx);
+ if (ctxlen > 128)
+ ctxlen = 128;
+
+ iov.iov_base = ub.buf;
+ iov.iov_len = sizeof(bl_message_t) + ctxlen;
+ ub.bl.bl_len = (uint32_t)iov.iov_len;
+ ub.bl.bl_version = BL_VERSION;
+ ub.bl.bl_type = (uint32_t)e;
+
+ if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1)
+ return -1;
+
+
+ ub.bl.bl_salen = slen;
+ memcpy(ub.bl.bl_data, ctx, ctxlen);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ msg.msg_control = ua.ctrl;
+ msg.msg_controllen = sizeof(ua.ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd));
+
+ tried = 0;
+again:
+ if (bl_init(b, false) == -1)
+ return -1;
+
+ if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) {
+ bl_reset(b, false);
+ goto again;
+ }
+ return tried >= NTRIES ? -1 : 0;
+}
+
+bl_info_t *
+bl_recv(bl_t b)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ union {
+ char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)];
+ uint32_t fd;
+ CRED_TYPE sc;
+ } ua;
+ struct cmsghdr *cmsg;
+ CRED_TYPE *sc;
+ union {
+ bl_message_t bl;
+ char buf[512];
+ } ub;
+ int got;
+ ssize_t rlen;
+ bl_info_t *bi = &b->b_info;
+
+ got = 0;
+ memset(bi, 0, sizeof(*bi));
+
+ iov.iov_base = ub.buf;
+ iov.iov_len = sizeof(ub);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ msg.msg_control = ua.ctrl;
+ msg.msg_controllen = sizeof(ua.ctrl) + 100;
+
+ rlen = recvmsg(b->b_fd, &msg, 0);
+ if (rlen == -1) {
+ bl_log(b->b_fun, LOG_ERR, "%s: recvmsg failed (%m)", __func__);
+ return NULL;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET) {
+ bl_log(b->b_fun, LOG_ERR,
+ "%s: unexpected cmsg_level %d",
+ __func__, cmsg->cmsg_level);
+ continue;
+ }
+ switch (cmsg->cmsg_type) {
+ case SCM_RIGHTS:
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
+ bl_log(b->b_fun, LOG_ERR,
+ "%s: unexpected cmsg_len %d != %zu",
+ __func__, cmsg->cmsg_len,
+ CMSG_LEN(2 * sizeof(int)));
+ continue;
+ }
+ memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd));
+ got |= GOT_FD;
+ break;
+#ifdef CRED_MESSAGE
+ case CRED_MESSAGE:
+ sc = (void *)CMSG_DATA(cmsg);
+ bi->bi_uid = sc->CRED_SC_UID;
+ bi->bi_gid = sc->CRED_SC_GID;
+ got |= GOT_CRED;
+ break;
+#endif
+ default:
+ bl_log(b->b_fun, LOG_ERR,
+ "%s: unexpected cmsg_type %d",
+ __func__, cmsg->cmsg_type);
+ continue;
+ }
+
+ }
+
+ if (got != (GOT_CRED|GOT_FD)) {
+ bl_log(b->b_fun, LOG_ERR, "message missing %s %s",
+#if GOT_CRED != 0
+ (got & GOT_CRED) == 0 ? "cred" :
+#endif
+ "", (got & GOT_FD) == 0 ? "fd" : "");
+
+ return NULL;
+ }
+
+ if ((size_t)rlen <= sizeof(ub.bl)) {
+ bl_log(b->b_fun, LOG_ERR, "message too short %zd", rlen);
+ return NULL;
+ }
+
+ if (ub.bl.bl_version != BL_VERSION) {
+ bl_log(b->b_fun, LOG_ERR, "bad version %d", ub.bl.bl_version);
+ return NULL;
+ }
+
+ bi->bi_type = ub.bl.bl_type;
+ bi->bi_slen = ub.bl.bl_salen;
+ bi->bi_ss = ub.bl.bl_ss;
+#ifndef CRED_MESSAGE
+ bi->bi_uid = -1;
+ bi->bi_gid = -1;
+#endif
+ strlcpy(bi->bi_msg, ub.bl.bl_data, MIN(sizeof(bi->bi_msg),
+ ((size_t)rlen - sizeof(ub.bl) + 1)));
+ return bi;
+}
diff --git a/lib/blacklist.c b/lib/blacklist.c
new file mode 100644
index 000000000000..b8a524ca301a
--- /dev/null
+++ b/lib/blacklist.c
@@ -0,0 +1,88 @@
+/* $NetBSD: blacklist.c,v 1.5 2015/01/22 16:19:53 christos Exp $ */
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: blacklist.c,v 1.5 2015/01/22 16:19:53 christos Exp $");
+
+#include <stdio.h>
+#include <bl.h>
+
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+int
+blacklist_sa(int action, int rfd, const struct sockaddr *sa, socklen_t salen,
+ const char *msg)
+{
+ struct blacklist *bl;
+ int rv;
+ if ((bl = blacklist_open()) == NULL)
+ return -1;
+ rv = blacklist_sa_r(bl, action, rfd, sa, salen, msg);
+ blacklist_close(bl);
+ return rv;
+}
+
+int
+blacklist_sa_r(struct blacklist *bl, int action, int rfd,
+ const struct sockaddr *sa, socklen_t slen, const char *msg)
+{
+ return bl_send(bl, action ? BL_ADD : BL_DELETE, rfd, sa, slen, msg);
+}
+
+int
+blacklist(int action, int rfd, const char *msg)
+{
+ return blacklist_sa(action, rfd, NULL, 0, msg);
+}
+
+int
+blacklist_r(struct blacklist *bl, int action, int rfd, const char *msg)
+{
+ return blacklist_sa_r(bl, action, rfd, NULL, 0, msg);
+}
+
+struct blacklist *
+blacklist_open(void) {
+ return bl_create(false, NULL, vsyslog);
+}
+
+void
+blacklist_close(struct blacklist *bl)
+{
+ bl_destroy(bl);
+}
diff --git a/lib/libblacklist.3 b/lib/libblacklist.3
new file mode 100644
index 000000000000..e13682031b81
--- /dev/null
+++ b/lib/libblacklist.3
@@ -0,0 +1,125 @@
+.\" $NetBSD: libblacklist.3,v 1.3 2015/01/25 23:09:28 wiz Exp $
+.\"
+.\" Copyright (c) 2015 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Christos Zoulas.
+.\"
+.\" 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``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 FOUNDATION OR CONTRIBUTORS
+.\" 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.
+.\"
+.Dd January 22, 2015
+.Dt LIBBLACKLIST 3
+.Os
+.Sh NAME
+.Nm blacklist_open ,
+.Nm blacklist_close ,
+.Nm blacklist_r ,
+.Nm blacklist ,
+.Nm blacklist_sa
+.Nm blacklist_sa_r ,
+.Nd Blacklistd notification library
+.Sh LIBRARY
+.Lb libblacklist
+.Sh SYNOPSIS
+.In blacklist.h
+.Ft struct blacklist *
+.Fn blacklist_open "void"
+.Ft void
+.Fn blacklist_close "struct blacklist *cookie"
+.Ft int
+.Fn blacklist "int action" "int fd" "const char *msg"
+.Ft int
+.Fn blacklist_r "struct blacklist *cookie" "int action" "int fd" "const char *msg"
+.Ft int
+.Fn blacklist_sa "int action" "int fd" "const struct sockaddr *sa" "socklen_t salen" "const char *msg"
+.Ft int
+.Fn blacklist_sa_r "struct blacklist *cookie" "int action" "int fd" "const struct sockaddr *sa" "socklen_t salen" "const char *msg"
+.Sh DESCRIPTION
+These functions can be used by daemons to notify
+.Xr blacklistd 8
+about successful and failed remote connections so that blacklistd can
+block or release port access to prevent Denial of Service attacks.
+.Pp
+The function
+.Fn blacklist_open
+creates a the necessary state to communicate with
+.Xr blacklistd 8
+and returns a pointer to it, or
+.Dv NULL
+on failure.
+.Pp
+The
+.Fn blacklist_close
+function frees all memory and resources used.
+.Pp
+The
+.Fn blacklist
+function sends a message to
+.Xr blacklistd 8 ,
+with an
+.Ar action
+argument specifying
+.Dv 1
+for a failed connection or
+.Dv 0
+for a successful connection,
+a file descriptor
+.Ar fd
+specifying the accepted file descriptor connected to the client,
+and an optional message in the
+.Ar msg
+argument.
+.Pp
+The
+.Fn blacklist_r
+function is more efficient because it keeps the blacklist state around.
+.Pp
+The
+.Fn blacklist_sa
+and
+.Fn blacklist_sa_r
+functions can be used with unconnected sockets, where
+.Xr getpeername 2
+will not work, the server will pass the peer name in the message.
+.Pp
+All functions log errors to
+.Xr syslogd 8 .
+.Sh RETURN VALUES
+The function
+.Fn bl_open
+returns a cookie on success and
+.Dv NULL
+on failure setting errno to an appropriate value.
+.Pp
+The
+.Fn bl_send
+function returns
+.Dv 0
+on success and
+.Dv -1
+on failure setting errno to an appropriate value.
+.Sh SEE ALSO
+.Xr blacklistd.conf 5 ,
+.Xr blacklistd 8
+.Sh AUTHORS
+.An Christos Zoulas
diff --git a/lib/shlib_version b/lib/shlib_version
new file mode 100644
index 000000000000..97c9f92d6b8f
--- /dev/null
+++ b/lib/shlib_version
@@ -0,0 +1,2 @@
+major=0
+minor=0
diff --git a/libexec/Makefile b/libexec/Makefile
new file mode 100644
index 000000000000..6537080bf465
--- /dev/null
+++ b/libexec/Makefile
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile,v 1.1 2015/01/22 17:49:41 christos Exp $
+
+SCRIPTS= blacklistd-helper
+SCRIPTSDIR= /libexec
+
+.include <bsd.prog.mk>
diff --git a/libexec/blacklistd-helper b/libexec/blacklistd-helper
new file mode 100644
index 000000000000..743ccf5508b3
--- /dev/null
+++ b/libexec/blacklistd-helper
@@ -0,0 +1,82 @@
+#!/bin/sh
+#echo "run $@" 1>&2
+#set -x
+# $1 command
+# $2 rulename
+# $3 protocol
+# $4 address
+# $5 mask
+# $6 port
+# $7 id
+
+pf=
+for f in npf pf; do
+ if [ -f "/etc/$f.conf" ]; then
+ pf="$f"
+ break
+ fi
+done
+
+if [ -z "$pf" ]; then
+ echo "$0: Unsupported packet filter" 1>&2
+ exit 1
+fi
+
+if [ -n "$3" ]; then
+ proto="proto $3"
+fi
+
+if [ -n "$6" ]; then
+ port="port $6"
+fi
+
+addr="$4"
+mask="$5"
+case "$4" in
+::ffff:*.*.*.*)
+ if [ "$5" = 128 ]; then
+ mask=32
+ addr=${4#::ffff:}
+ fi;;
+esac
+
+case "$1" in
+add)
+ case "$pf" in
+ npf)
+ /sbin/npfctl rule "$2" add block in final $proto from \
+ "$addr/$mask" to any $port
+ ;;
+ pf)
+ # insert $ip/$mask into per-protocol anchored table
+ /sbin/pfctl -a "$2" -t "port$6" -T add "$addr/$mask"
+ echo "block in quick $proto from <port$6> to any $port" | \
+ /sbin/pfctl -a "$2" -f -
+ ;;
+ esac
+ ;;
+rem)
+ case "$pf" in
+ npf)
+ /sbin/npfctl rule "$2" rem-id "$7"
+ ;;
+ pf)
+ /sbin/pfctl -a "$2" -t "port$6" -T delete "$addr/$mask"
+ ;;
+ esac
+ ;;
+flush)
+ case "$pf" in
+ npf)
+ /sbin/npfctl rule "$2" flush
+ ;;
+ pf)
+ /sbin/pfctl -a "$2" -t "port$6" -T flush
+ ;;
+ esac
+ ;;
+*)
+ echo "$0: Unknown command '$1'" 1>&2
+ exit 1
+ ;;
+esac
diff --git a/port/Makefile.am b/port/Makefile.am
new file mode 100644
index 000000000000..f1b36ca880a3
--- /dev/null
+++ b/port/Makefile.am
@@ -0,0 +1,25 @@
+#
+ACLOCAL_AMFLAGS = -I m4
+lib_LTLIBRARIES = libblacklist.la
+include_HEADERS = blacklist.h
+
+bin_PROGRAMS = blacklistd blacklistctl srvtest cltest
+
+VPATH = ../bin:../lib:../test
+
+AM_CPPFLAGS = -I../include -DDOT="."
+AM_CFLAGS = @WARNINGS@
+
+libblacklist_la_SOURCES = bl.c blacklist.c
+libblacklist_la_LDFLAGS = -no-undefined -version-info 0:0:0
+libblacklist_la_LIBADD = $(LTLIBOBJS)
+
+SRCS = internal.c support.c run.c conf.c state.c
+blacklistd_SOURCES = blacklistd.c ${SRCS}
+blacklistd_LDADD = libblacklist.la
+blacklistctl_SOURCES = blacklistctl.c ${SRCS}
+blacklistctl_LDADD = libblacklist.la
+srvtest_SOURCES = srvtest.c ${SRCS}
+srvtest_LDADD = libblacklist.la
+cltest_SOURCES = cltest.c ${SRCS}
+cltest_LDADD = libblacklist.la
diff --git a/port/_strtoi.h b/port/_strtoi.h
new file mode 100644
index 000000000000..4b2b4e80f0d8
--- /dev/null
+++ b/port/_strtoi.h
@@ -0,0 +1,93 @@
+/* $NetBSD: _strtoi.h,v 1.1 2015/01/22 02:15:59 christos Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ * Original version ID:
+ * NetBSD: src/lib/libc/locale/_wcstoul.h,v 1.2 2003/08/07 16:43:03 agc Exp
+ *
+ * Created by Kamil Rytarowski, based on ID:
+ * NetBSD: src/common/lib/libc/stdlib/_strtoul.h,v 1.7 2013/05/17 12:55:56 joerg Exp
+ */
+
+/*
+ * function template for strtoi and strtou
+ *
+ * parameters:
+ * _FUNCNAME : function name
+ * __TYPE : return and range limits type
+ * __WRAPPED : wrapped function, strtoimax or strtoumax
+ */
+
+__TYPE
+_FUNCNAME(const char * __restrict nptr, char ** __restrict endptr, int base,
+ __TYPE lo, __TYPE hi, int * rstatus)
+{
+ int serrno;
+ __TYPE im;
+ char *ep;
+ int rep;
+
+ /* endptr may be NULL */
+
+ if (endptr == NULL)
+ endptr = &ep;
+
+ if (rstatus == NULL)
+ rstatus = &rep;
+
+ serrno = errno;
+ errno = 0;
+
+ im = __WRAPPED(nptr, endptr, base);
+
+ *rstatus = errno;
+ errno = serrno;
+
+ if (*rstatus == 0) {
+ /* No digits were found */
+ if (nptr == *endptr)
+ *rstatus = ECANCELED;
+ /* There are further characters after number */
+ else if (**endptr != '\0')
+ *rstatus = ENOTSUP;
+ }
+
+ if (im < lo) {
+ if (*rstatus == 0)
+ *rstatus = ERANGE;
+ return lo;
+ }
+ if (im > hi) {
+ if (*rstatus == 0)
+ *rstatus = ERANGE;
+ return hi;
+ }
+
+ return im;
+}
diff --git a/port/clock_gettime.c b/port/clock_gettime.c
new file mode 100644
index 000000000000..284bd8176901
--- /dev/null
+++ b/port/clock_gettime.c
@@ -0,0 +1,17 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <time.h>
+#include <sys/time.h>
+
+int
+clock_gettime(int clock __unused, struct timespec *ts)
+{
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == -1)
+ return -1;
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
+ return 0;
+}
diff --git a/port/config.h b/port/config.h
new file mode 100644
index 000000000000..27f32636de01
--- /dev/null
+++ b/port/config.h
@@ -0,0 +1,3 @@
+#if defined(__FreeBSD__)
+#include "port.h"
+#endif
diff --git a/port/configure.ac b/port/configure.ac
new file mode 100644
index 000000000000..eef8065f060a
--- /dev/null
+++ b/port/configure.ac
@@ -0,0 +1,91 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT([blacklistd],[0.1],[christos@netbsd.com])
+AM_INIT_AUTOMAKE([subdir-objects foreign])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_SUBST(WARNINGS)
+
+dnl Checks for programs.
+AC_PROG_CC_STDC
+AC_USE_SYSTEM_EXTENSIONS
+AM_PROG_CC_C_O
+AC_C_BIGENDIAN
+AC_PROG_INSTALL
+AC_PROG_LN_S
+LT_INIT([disable-static pic-only])
+gl_VISIBILITY
+dnl Checks for headers
+AC_HEADER_STDC
+AC_HEADER_MAJOR
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(stdint.h fcntl.h stdint.h inttypes.h unistd.h)
+AC_CHECK_HEADERS(sys/un.h sys/socket.h limits.h)
+AC_CHECK_HEADERS(arpa/inet.h getopt.h err.h)
+AC_CHECK_HEADERS(sys/types.h util.h sys/time.h time.h)
+AC_CHECK_HEADERS(netatalk/at.h net/if_dl.h db.h db_185.h)
+AC_CHECK_LIB(rt, clock_gettime)
+AC_CHECK_LIB(db, __db185_open)
+AC_CHECK_LIB(util, pidfile)
+AC_CHECK_LIB(util, sockaddr_snprintf)
+
+AH_BOTTOM([
+#ifndef __NetBSD__
+#include "port.h"
+#endif
+])
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_SYS_LARGEFILE
+AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [#include <sys/socket.h>])
+
+AC_TYPE_PID_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_INT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_INT64_T
+AC_TYPE_INTPTR_T
+AC_TYPE_UINTPTR_T
+
+AC_MSG_CHECKING(for gcc compiler warnings)
+AC_ARG_ENABLE(warnings,
+[ --disable-warnings disable compiler warnings],
+[if test "${enableval}" = no -o "$GCC" = no; then
+ AC_MSG_RESULT(no)
+ WARNINGS=
+else
+ AC_MSG_RESULT(yes)
+ WARNINGS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith \
+ -Wmissing-declarations -Wredundant-decls -Wnested-externs \
+ -Wsign-compare -Wreturn-type -Wswitch -Wshadow \
+ -Wcast-qual -Wwrite-strings -Wextra -Wunused-parameter -Wformat=2"
+fi], [
+if test "$GCC" = yes; then
+ AC_MSG_RESULT(yes)
+ WARNINGS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith \
+ -Wmissing-declarations -Wredundant-decls -Wnested-externs \
+ -Wsign-compare -Wreturn-type -Wswitch -Wshadow \
+ -Wcast-qual -Wwrite-strings -Wextra -Wunused-parameter -Wformat=2"
+else
+ WARNINGS=
+ AC_MSG_RESULT(no)
+fi])
+
+dnl Checks for functions
+AC_CHECK_FUNCS(strerror)
+
+dnl Provide implementation of some required functions if necessary
+AC_REPLACE_FUNCS(strtoi sockaddr_snprintf popenve clock_gettime strlcpy strlcat getprogname fparseln fgetln pidfile)
+
+dnl See if we are cross-compiling
+AM_CONDITIONAL(IS_CROSS_COMPILE, test "$cross_compiling" = yes)
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/port/fgetln.c b/port/fgetln.c
new file mode 100644
index 000000000000..a41a383a6653
--- /dev/null
+++ b/port/fgetln.c
@@ -0,0 +1,106 @@
+/* $NetBSD: fgetln.c,v 1.1 2015/01/22 03:48:07 christos Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if !HAVE_FGETLN
+#include <stdlib.h>
+#ifndef HAVE_NBTOOL_CONFIG_H
+/* These headers are required, but included from nbtool_config.h */
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#endif
+
+char *
+fgetln(FILE *fp, size_t *len)
+{
+ static char *buf = NULL;
+ static size_t bufsiz = 0;
+ char *ptr;
+
+
+ if (buf == NULL) {
+ bufsiz = BUFSIZ;
+ if ((buf = malloc(bufsiz)) == NULL)
+ return NULL;
+ }
+
+ if (fgets(buf, bufsiz, fp) == NULL)
+ return NULL;
+
+ *len = 0;
+ while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
+ size_t nbufsiz = bufsiz + BUFSIZ;
+ char *nbuf = realloc(buf, nbufsiz);
+
+ if (nbuf == NULL) {
+ int oerrno = errno;
+ free(buf);
+ errno = oerrno;
+ buf = NULL;
+ return NULL;
+ } else
+ buf = nbuf;
+
+ if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) {
+ buf[bufsiz] = '\0';
+ *len = strlen(buf);
+ return buf;
+ }
+
+ *len = bufsiz;
+ bufsiz = nbufsiz;
+ }
+
+ *len = (ptr - buf) + 1;
+ return buf;
+}
+
+#endif
+
+#ifdef TEST
+int
+main(int argc, char *argv[])
+{
+ char *p;
+ size_t len;
+
+ while ((p = fgetln(stdin, &len)) != NULL) {
+ (void)printf("%zu %s", len, p);
+ free(p);
+ }
+ return 0;
+}
+#endif
diff --git a/port/fparseln.c b/port/fparseln.c
new file mode 100644
index 000000000000..5bfae54b9a9b
--- /dev/null
+++ b/port/fparseln.c
@@ -0,0 +1,236 @@
+/* $NetBSD: fparseln.c,v 1.1 2015/01/22 03:48:07 christos Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: fparseln.c,v 1.1 2015/01/22 03:48:07 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if ! HAVE_FPARSELN || BROKEN_FPARSELN
+
+#define FLOCKFILE(fp)
+#define FUNLOCKFILE(fp)
+
+#if defined(_REENTRANT) && !HAVE_NBTOOL_CONFIG_H
+#define __fgetln(f, l) __fgetstr(f, l, '\n')
+#else
+#define __fgetln(f, l) fgetln(f, l)
+#endif
+
+static int isescaped(const char *, const char *, int);
+
+/* isescaped():
+ * Return true if the character in *p that belongs to a string
+ * that starts in *sp, is escaped by the escape character esc.
+ */
+static int
+isescaped(const char *sp, const char *p, int esc)
+{
+ const char *cp;
+ size_t ne;
+
+ /* No escape character */
+ if (esc == '\0')
+ return 0;
+
+ /* Count the number of escape characters that precede ours */
+ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
+ continue;
+
+ /* Return true if odd number of escape characters */
+ return (ne & 1) != 0;
+}
+
+
+/* fparseln():
+ * Read a line from a file parsing continuations ending in \
+ * and eliminating trailing newlines, or comments starting with
+ * the comment char.
+ */
+char *
+fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags)
+{
+ static const char dstr[3] = { '\\', '\\', '#' };
+
+ size_t s, len;
+ char *buf;
+ char *ptr, *cp;
+ int cnt;
+ char esc, con, nl, com;
+
+ len = 0;
+ buf = NULL;
+ cnt = 1;
+
+ if (str == NULL)
+ str = dstr;
+
+ esc = str[0];
+ con = str[1];
+ com = str[2];
+ /*
+ * XXX: it would be cool to be able to specify the newline character,
+ * but unfortunately, fgetln does not let us
+ */
+ nl = '\n';
+
+ FLOCKFILE(fp);
+
+ while (cnt) {
+ cnt = 0;
+
+ if (lineno)
+ (*lineno)++;
+
+ if ((ptr = __fgetln(fp, &s)) == NULL)
+ break;
+
+ if (s && com) { /* Check and eliminate comments */
+ for (cp = ptr; cp < ptr + s; cp++)
+ if (*cp == com && !isescaped(ptr, cp, esc)) {
+ s = cp - ptr;
+ cnt = s == 0 && buf == NULL;
+ break;
+ }
+ }
+
+ if (s && nl) { /* Check and eliminate newlines */
+ cp = &ptr[s - 1];
+
+ if (*cp == nl)
+ s--; /* forget newline */
+ }
+
+ if (s && con) { /* Check and eliminate continuations */
+ cp = &ptr[s - 1];
+
+ if (*cp == con && !isescaped(ptr, cp, esc)) {
+ s--; /* forget continuation char */
+ cnt = 1;
+ }
+ }
+
+ if (s == 0) {
+ /*
+ * nothing to add, skip realloc except in case
+ * we need a minimal buf to return an empty line
+ */
+ if (cnt || buf != NULL)
+ continue;
+ }
+
+ if ((cp = realloc(buf, len + s + 1)) == NULL) {
+ FUNLOCKFILE(fp);
+ free(buf);
+ return NULL;
+ }
+ buf = cp;
+
+ (void) memcpy(buf + len, ptr, s);
+ len += s;
+ buf[len] = '\0';
+ }
+
+ FUNLOCKFILE(fp);
+
+ if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
+ strchr(buf, esc) != NULL) {
+ ptr = cp = buf;
+ while (cp[0] != '\0') {
+ int skipesc;
+
+ while (cp[0] != '\0' && cp[0] != esc)
+ *ptr++ = *cp++;
+ if (cp[0] == '\0' || cp[1] == '\0')
+ break;
+
+ skipesc = 0;
+ if (cp[1] == com)
+ skipesc += (flags & FPARSELN_UNESCCOMM);
+ if (cp[1] == con)
+ skipesc += (flags & FPARSELN_UNESCCONT);
+ if (cp[1] == esc)
+ skipesc += (flags & FPARSELN_UNESCESC);
+ if (cp[1] != com && cp[1] != con && cp[1] != esc)
+ skipesc = (flags & FPARSELN_UNESCREST);
+
+ if (skipesc)
+ cp++;
+ else
+ *ptr++ = *cp++;
+ *ptr++ = *cp++;
+ }
+ *ptr = '\0';
+ len = strlen(buf);
+ }
+
+ if (size)
+ *size = len;
+ return buf;
+}
+
+#ifdef TEST
+
+int main(int, char **);
+
+int
+main(int argc, char **argv)
+{
+ char *ptr;
+ size_t size, line;
+
+ line = 0;
+ while ((ptr = fparseln(stdin, &size, &line, NULL,
+ FPARSELN_UNESCALL)) != NULL)
+ printf("line %d (%d) |%s|\n", line, size, ptr);
+ return 0;
+}
+
+/*
+
+# This is a test
+line 1
+line 2 \
+line 3 # Comment
+line 4 \# Not comment \\\\
+
+# And a comment \
+line 5 \\\
+line 6
+
+*/
+
+#endif /* TEST */
+#endif /* ! HAVE_FPARSELN || BROKEN_FPARSELN */
diff --git a/port/getprogname.c b/port/getprogname.c
new file mode 100644
index 000000000000..983d6740c88c
--- /dev/null
+++ b/port/getprogname.c
@@ -0,0 +1,24 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <string.h>
+
+extern char *__progname;
+
+const char *
+getprogname(void)
+{
+ return __progname;
+}
+
+void
+setprogname(char *p)
+{
+ char *q;
+ if (p == NULL)
+ return;
+ if ((q = strrchr(p, '/')) != NULL)
+ __progname = ++q;
+ else
+ __progname = p;
+}
diff --git a/port/m4/.cvsignore b/port/m4/.cvsignore
new file mode 100644
index 000000000000..0f4126cd6718
--- /dev/null
+++ b/port/m4/.cvsignore
@@ -0,0 +1 @@
+*.m4
diff --git a/port/pidfile.c b/port/pidfile.c
new file mode 100644
index 000000000000..4deb2349d20a
--- /dev/null
+++ b/port/pidfile.c
@@ -0,0 +1,183 @@
+/* $NetBSD: pidfile.c,v 1.2 2016/04/05 12:28:57 christos Exp $ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe, Matthias Scheler and Julio Merino.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: pidfile.c,v 1.2 2016/04/05 12:28:57 christos Exp $");
+#endif
+
+#include <sys/param.h>
+
+#include <paths.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+static pid_t pidfile_pid;
+static char *pidfile_path;
+
+/* Deletes an existent pidfile iff it was created by this process. */
+static void
+pidfile_cleanup(void)
+{
+
+ if ((pidfile_path != NULL) && (pidfile_pid == getpid()))
+ (void) unlink(pidfile_path);
+}
+
+/* Registers an atexit(3) handler to delete the pidfile we have generated.
+ * We only register the handler when we create a pidfile, so we can assume
+ * that the pidfile exists.
+ *
+ * Returns 0 on success or -1 if the handler could not be registered. */
+static int
+register_atexit_handler(void)
+{
+ static bool done = false;
+
+ if (!done) {
+ if (atexit(pidfile_cleanup) < 0)
+ return -1;
+ done = true;
+ }
+
+ return 0;
+}
+
+/* Given a new pidfile name in 'path', deletes any previously-created pidfile
+ * if the previous file differs to the new one.
+ *
+ * If a previous file is deleted, returns 1, which means that a new pidfile
+ * must be created. Otherwise, this returns 0, which means that the existing
+ * file does not need to be touched. */
+static int
+cleanup_old_pidfile(const char* path)
+{
+ if (pidfile_path != NULL) {
+ if (strcmp(pidfile_path, path) != 0) {
+ pidfile_cleanup();
+
+ free(pidfile_path);
+ pidfile_path = NULL;
+
+ return 1;
+ } else
+ return 0;
+ } else
+ return 1;
+}
+
+/* Constructs a name for a pidfile in the default location (/var/run). If
+ * 'basename' is NULL, uses the name of the current program for the name of
+ * the pidfile.
+ *
+ * Returns a pointer to a dynamically-allocatd string containing the absolute
+ * path to the pidfile; NULL on failure. */
+static char *
+generate_varrun_path(const char *bname)
+{
+ char *path;
+
+ if (bname == NULL)
+ bname = getprogname();
+
+ /* _PATH_VARRUN includes trailing / */
+ if (asprintf(&path, "%s%s.pid", _PATH_VARRUN, bname) == -1)
+ return NULL;
+ return path;
+}
+
+/* Creates a pidfile with the provided name. The new pidfile is "registered"
+ * in the global variables pidfile_path and pidfile_pid so that any further
+ * call to pidfile(3) can check if we are recreating the same file or a new
+ * one.
+ *
+ * Returns 0 on success or -1 if there is any error. */
+static int
+create_pidfile(const char* path)
+{
+ FILE *f;
+
+ if (register_atexit_handler() == -1)
+ return -1;
+
+ if (cleanup_old_pidfile(path) == 0)
+ return 0;
+
+ pidfile_path = strdup(path);
+ if (pidfile_path == NULL)
+ return -1;
+
+ if ((f = fopen(path, "w")) == NULL) {
+ free(pidfile_path);
+ pidfile_path = NULL;
+ return -1;
+ }
+
+ pidfile_pid = getpid();
+
+ (void) fprintf(f, "%d\n", pidfile_pid);
+ (void) fclose(f);
+
+ return 0;
+}
+
+int
+pidfile(const char *path)
+{
+
+ if (path == NULL || strchr(path, '/') == NULL) {
+ char *default_path;
+
+ if ((default_path = generate_varrun_path(path)) == NULL)
+ return -1;
+
+ if (create_pidfile(default_path) == -1) {
+ free(default_path);
+ return -1;
+ }
+
+ free(default_path);
+ return 0;
+ } else
+ return create_pidfile(path);
+}
diff --git a/port/popenve.c b/port/popenve.c
new file mode 100644
index 000000000000..20f6b5b86b68
--- /dev/null
+++ b/port/popenve.c
@@ -0,0 +1,274 @@
+/* $NetBSD: popenve.c,v 1.2 2015/01/22 03:10:50 christos Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 5/3/95";
+#else
+__RCSID("$NetBSD: popenve.c,v 1.2 2015/01/22 03:10:50 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef __weak_alias
+__weak_alias(popen,_popen)
+__weak_alias(pclose,_pclose)
+#endif
+
+static struct pid {
+ struct pid *next;
+ FILE *fp;
+#ifdef _REENTRANT
+ int fd;
+#endif
+ pid_t pid;
+} *pidlist;
+
+#ifdef _REENTRANT
+static rwlock_t pidlist_lock = RWLOCK_INITIALIZER;
+#endif
+
+static struct pid *
+pdes_get(int *pdes, const char **type)
+{
+ struct pid *cur;
+ int flags = strchr(*type, 'e') ? O_CLOEXEC : 0;
+ int serrno;
+
+ if (strchr(*type, '+')) {
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+ int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM;
+ *type = "r+";
+ if (socketpair(AF_LOCAL, stype, 0, pdes) < 0)
+ return NULL;
+#if SOCK_CLOEXEC == 0
+ fcntl(pdes[0], F_SETFD, FD_CLOEXEC);
+ fcntl(pdes[1], F_SETFD, FD_CLOEXEC);
+#endif
+ } else {
+ *type = strrchr(*type, 'r') ? "r" : "w";
+#if SOCK_CLOEXEC != 0
+ if (pipe2(pdes, flags) == -1)
+ return NULL;
+#else
+ if (pipe(pdes) == -1)
+ return NULL;
+ fcntl(pdes[0], F_SETFL, fcntl(pdes[0], F_GETFL) | flags);
+ fcntl(pdes[1], F_SETFL, fcntl(pdes[1], F_GETFL) | flags);
+#endif
+ }
+
+ if ((cur = malloc(sizeof(*cur))) != NULL)
+ return cur;
+ serrno = errno;
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ errno = serrno;
+ return NULL;
+}
+
+static void
+pdes_child(int *pdes, const char *type)
+{
+ struct pid *old;
+
+ /* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
+ from previous popen() calls that remain open in the
+ parent process are closed in the new child process. */
+ for (old = pidlist; old; old = old->next)
+#ifdef _REENTRANT
+ (void)close(old->fd); /* don't allow a flush */
+#else
+ (void)close(fileno(old->fp)); /* don't allow a flush */
+#endif
+
+ if (type[0] == 'r') {
+ (void)close(pdes[0]);
+ if (pdes[1] != STDOUT_FILENO) {
+ (void)dup2(pdes[1], STDOUT_FILENO);
+ (void)close(pdes[1]);
+ }
+ if (type[1] == '+')
+ (void)dup2(STDOUT_FILENO, STDIN_FILENO);
+ } else {
+ (void)close(pdes[1]);
+ if (pdes[0] != STDIN_FILENO) {
+ (void)dup2(pdes[0], STDIN_FILENO);
+ (void)close(pdes[0]);
+ }
+ }
+}
+
+static void
+pdes_parent(int *pdes, struct pid *cur, pid_t pid, const char *type)
+{
+ FILE *iop;
+
+ /* Parent; assume fdopen can't fail. */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+#ifdef _REENTRANT
+ cur->fd = pdes[0];
+#endif
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+#ifdef _REENTRANT
+ cur->fd = pdes[1];
+#endif
+ (void)close(pdes[0]);
+ }
+
+ /* Link into list of file descriptors. */
+ cur->fp = iop;
+ cur->pid = pid;
+ cur->next = pidlist;
+ pidlist = cur;
+}
+
+static void
+pdes_error(int *pdes, struct pid *cur)
+{
+ free(cur);
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+}
+
+FILE *
+popenve(const char *cmd, char *const *argv, char *const *envp, const char *type)
+{
+ struct pid *cur;
+ int pdes[2], serrno;
+ pid_t pid;
+
+ if ((cur = pdes_get(pdes, &type)) == NULL)
+ return NULL;
+
+#ifdef _REENTRANT
+ (void)rwlock_rdlock(&pidlist_lock);
+#endif
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ serrno = errno;
+#ifdef _REENTRANT
+ (void)rwlock_unlock(&pidlist_lock);
+#endif
+ pdes_error(pdes, cur);
+ errno = serrno;
+ return NULL;
+ /* NOTREACHED */
+ case 0: /* Child. */
+ pdes_child(pdes, type);
+ execve(cmd, argv, envp);
+ _exit(127);
+ /* NOTREACHED */
+ }
+
+ pdes_parent(pdes, cur, pid, type);
+
+#ifdef _REENTRANT
+ (void)rwlock_unlock(&pidlist_lock);
+#endif
+
+ return cur->fp;
+}
+
+/*
+ * pclose --
+ * Pclose returns -1 if stream is not associated with a `popened' command,
+ * if already `pclosed', or waitpid returns an error.
+ */
+int
+pcloseve(FILE *iop)
+{
+ struct pid *cur, *last;
+ int pstat;
+ pid_t pid;
+
+#ifdef _REENTRANT
+ rwlock_wrlock(&pidlist_lock);
+#endif
+
+ /* Find the appropriate file pointer. */
+ for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
+ if (cur->fp == iop)
+ break;
+ if (cur == NULL) {
+#ifdef _REENTRANT
+ (void)rwlock_unlock(&pidlist_lock);
+#endif
+ errno = ESRCH;
+ return -1;
+ }
+
+ (void)fclose(iop);
+
+ /* Remove the entry from the linked list. */
+ if (last == NULL)
+ pidlist = cur->next;
+ else
+ last->next = cur->next;
+
+#ifdef _REENTRANT
+ (void)rwlock_unlock(&pidlist_lock);
+#endif
+
+ do {
+ pid = waitpid(cur->pid, &pstat, 0);
+ } while (pid == -1 && errno == EINTR);
+
+ free(cur);
+
+ return pid == -1 ? -1 : pstat;
+}
diff --git a/port/port.h b/port/port.h
new file mode 100644
index 000000000000..f82fb34b40f5
--- /dev/null
+++ b/port/port.h
@@ -0,0 +1,86 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <inttypes.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <netdb.h>
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+#ifndef __dead
+#define __dead __attribute__((__noreturn__))
+#endif
+
+#ifndef __RCSID
+#define __RCSID(a)
+#endif
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(intptr_t)(a))
+#endif
+
+#ifndef __arraycount
+#define __arraycount(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_POPENVE
+FILE *popenve(const char *, char *const *, char *const *, const char *);
+int pcloseve(FILE *);
+#define pclose(a) pcloseve(a);
+#endif
+
+#ifndef HAVE_SOCKADDR_SNPRINTF
+struct sockaddr;
+int sockaddr_snprintf(char *, size_t, const char *, const struct sockaddr *);
+#endif
+
+#ifndef HAVE_STRTOI
+intmax_t strtoi(const char *, char **, int, intmax_t, intmax_t, int *);
+#endif
+
+#ifndef HAVE_GETPROGNAME
+const char *getprogname(void);
+void setprogname(char *);
+#endif
+
+#ifndef HAVE_PIDFILE
+int pidfile(const char *);
+#endif
+
+#ifndef HAVE_FPARSELN
+#define FPARSELN_UNESCALL 0xf
+#define FPARSELN_UNESCCOMM 0x1
+#define FPARSELN_UNESCCONT 0x2
+#define FPARSELN_UNESCESC 0x4
+#define FPARSELN_UNESCREST 0x8
+char *fparseln(FILE *, size_t *, size_t *, const char delim[3], int);
+#endif
+
+#ifndef HAVE_FGETLN
+char *fgetln(FILE *, size_t *);
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+struct timespec;
+int clock_gettime(int, struct timespec *);
+#define CLOCK_REALTIME 0
+#endif
+
+#if !defined(__FreeBSD__)
+#define _PATH_BLCONF "conf"
+#define _PATH_BLCONTROL "control"
+#define _PATH_BLSOCK "blacklistd.sock"
+#define _PATH_BLSTATE "blacklistd.db"
+#endif
diff --git a/port/sockaddr_snprintf.c b/port/sockaddr_snprintf.c
new file mode 100644
index 000000000000..41e5e0c0ed20
--- /dev/null
+++ b/port/sockaddr_snprintf.c
@@ -0,0 +1,383 @@
+/* $NetBSD: sockaddr_snprintf.c,v 1.10 2016/04/05 12:28:57 christos Exp $ */
+
+/*-
+ * Copyright (c) 2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: sockaddr_snprintf.c,v 1.10 2016/04/05 12:28:57 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#ifdef __linux__
+#undef HAVE_NETATALK_AT_H
+#endif
+#ifdef HAVE_NETATALK_AT_H
+#include <netatalk/at.h>
+#endif
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#include <netdb.h>
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+#define SLEN(a) (a)->a ## _len
+#else
+static socklen_t
+socklen(u_int af)
+{
+ switch (af) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ case AF_LOCAL:
+ return sizeof(struct sockaddr_un);
+#ifdef HAVE_NET_IF_DL_H
+ case AF_LINK:
+ return sizeof(struct sockaddr_dl);
+#endif
+#ifdef HAVE_NETATALK_AT_H
+ case AF_APPLETALK:
+ return sizeof(struct sockaddr_at);
+#endif
+ default:
+ return sizeof(struct sockaddr_storage);
+ }
+}
+
+#define SLEN(a) socklen((a)->a ## _family)
+#endif
+
+#ifdef HAVE_NETATALK_AT_H
+static int
+debug_at(char *str, size_t len, const struct sockaddr_at *sat)
+{
+ return snprintf(str, len, "sat_len=%u, sat_family=%u, sat_port=%u, "
+ "sat_addr.s_net=%u, sat_addr.s_node=%u, "
+ "sat_range.r_netrange.nr_phase=%u, "
+ "sat_range.r_netrange.nr_firstnet=%u, "
+ "sat_range.r_netrange.nr_lastnet=%u",
+ SLEN(sat), sat->sat_family, sat->sat_port,
+ sat->sat_addr.s_net, sat->sat_addr.s_node,
+ sat->sat_range.r_netrange.nr_phase,
+ sat->sat_range.r_netrange.nr_firstnet,
+ sat->sat_range.r_netrange.nr_lastnet);
+}
+#endif
+
+static int
+debug_in(char *str, size_t len, const struct sockaddr_in *sin)
+{
+ return snprintf(str, len, "sin_len=%u, sin_family=%u, sin_port=%u, "
+ "sin_addr.s_addr=%08x",
+ SLEN(sin), sin->sin_family, sin->sin_port,
+ sin->sin_addr.s_addr);
+}
+
+static int
+debug_in6(char *str, size_t len, const struct sockaddr_in6 *sin6)
+{
+ const uint8_t *s = sin6->sin6_addr.s6_addr;
+
+ return snprintf(str, len, "sin6_len=%u, sin6_family=%u, sin6_port=%u, "
+ "sin6_flowinfo=%u, "
+ "sin6_addr=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x:%02x:%02x, sin6_scope_id=%u",
+ SLEN(sin6), sin6->sin6_family, sin6->sin6_port,
+ sin6->sin6_flowinfo, s[0x0], s[0x1], s[0x2], s[0x3], s[0x4], s[0x5],
+ s[0x6], s[0x7], s[0x8], s[0x9], s[0xa], s[0xb], s[0xc], s[0xd],
+ s[0xe], s[0xf], sin6->sin6_scope_id);
+}
+
+static int
+debug_un(char *str, size_t len, const struct sockaddr_un *sun)
+{
+ return snprintf(str, len, "sun_len=%u, sun_family=%u, sun_path=%*s",
+ SLEN(sun), sun->sun_family, (int)sizeof(sun->sun_path),
+ sun->sun_path);
+}
+
+#ifdef HAVE_NET_IF_DL_H
+static int
+debug_dl(char *str, size_t len, const struct sockaddr_dl *sdl)
+{
+ const uint8_t *s = (const void *)sdl->sdl_data;
+
+ return snprintf(str, len, "sdl_len=%u, sdl_family=%u, sdl_index=%u, "
+ "sdl_type=%u, sdl_nlen=%u, sdl_alen=%u, sdl_slen=%u, sdl_data="
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ SLEN(sdl), sdl->sdl_family, sdl->sdl_index,
+ sdl->sdl_type, sdl->sdl_nlen, sdl->sdl_alen, sdl->sdl_slen,
+ s[0x0], s[0x1], s[0x2], s[0x3], s[0x4], s[0x5],
+ s[0x6], s[0x7], s[0x8], s[0x9], s[0xa], s[0xb]);
+}
+#endif
+
+int
+sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt,
+ const struct sockaddr * const sa)
+{
+ const void *a = NULL;
+ char abuf[1024], nbuf[1024], *addr = NULL;
+
+ char Abuf[1024], pbuf[32], *name = NULL, *port = NULL;
+ char *ebuf = &sbuf[len - 1], *buf = sbuf;
+ const char *ptr, *s;
+ int p = -1;
+#ifdef HAVE_NETATALK_AT_H
+ const struct sockaddr_at *sat = NULL;
+#endif
+ const struct sockaddr_in *sin4 = NULL;
+ const struct sockaddr_in6 *sin6 = NULL;
+ const struct sockaddr_un *sun = NULL;
+#ifdef HAVE_NET_IF_DL_H
+ const struct sockaddr_dl *sdl = NULL;
+ char *w = NULL;
+#endif
+ int na = 1;
+
+#define ADDC(c) do { if (buf < ebuf) *buf++ = c; else buf++; } \
+ while (/*CONSTCOND*/0)
+#define ADDS(p) do { for (s = p; *s; s++) ADDC(*s); } \
+ while (/*CONSTCOND*/0)
+#define ADDNA() do { if (na) ADDS("N/A"); } \
+ while (/*CONSTCOND*/0)
+
+ switch (sa->sa_family) {
+ case AF_UNSPEC:
+ goto done;
+#ifdef HAVE_NETATALK_AT_H
+ case AF_APPLETALK:
+ sat = ((const struct sockaddr_at *)(const void *)sa);
+ p = ntohs(sat->sat_port);
+ (void)snprintf(addr = abuf, sizeof(abuf), "%u.%u",
+ ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
+ (void)snprintf(port = pbuf, sizeof(pbuf), "%d", p);
+ break;
+#endif
+ case AF_LOCAL:
+ sun = ((const struct sockaddr_un *)(const void *)sa);
+ (void)strlcpy(addr = abuf, sun->sun_path, sizeof(abuf));
+ break;
+ case AF_INET:
+ sin4 = ((const struct sockaddr_in *)(const void *)sa);
+ p = ntohs(sin4->sin_port);
+ a = &sin4->sin_addr;
+ break;
+ case AF_INET6:
+ sin6 = ((const struct sockaddr_in6 *)(const void *)sa);
+ p = ntohs(sin6->sin6_port);
+ a = &sin6->sin6_addr;
+ break;
+#ifdef HAVE_NET_IF_DL_H
+ case AF_LINK:
+ sdl = ((const struct sockaddr_dl *)(const void *)sa);
+ (void)strlcpy(addr = abuf, link_ntoa(sdl), sizeof(abuf));
+ if ((w = strchr(addr, ':')) != 0) {
+ *w++ = '\0';
+ addr = w;
+ }
+ break;
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ if (addr == abuf)
+ name = addr;
+
+ if (a && getnameinfo(sa, (socklen_t)SLEN(sa), addr = abuf,
+ (unsigned int)sizeof(abuf), NULL, 0,
+ NI_NUMERICHOST|NI_NUMERICSERV) != 0)
+ return -1;
+
+ for (ptr = fmt; *ptr; ptr++) {
+ if (*ptr != '%') {
+ ADDC(*ptr);
+ continue;
+ }
+ next_char:
+ switch (*++ptr) {
+ case '?':
+ na = 0;
+ goto next_char;
+ case 'a':
+ ADDS(addr);
+ break;
+ case 'p':
+ if (p != -1) {
+ (void)snprintf(nbuf, sizeof(nbuf), "%d", p);
+ ADDS(nbuf);
+ } else
+ ADDNA();
+ break;
+ case 'f':
+ (void)snprintf(nbuf, sizeof(nbuf), "%d", sa->sa_family);
+ ADDS(nbuf);
+ break;
+ case 'l':
+ (void)snprintf(nbuf, sizeof(nbuf), "%d", SLEN(sa));
+ ADDS(nbuf);
+ break;
+ case 'A':
+ if (name)
+ ADDS(name);
+ else if (!a)
+ ADDNA();
+ else {
+ getnameinfo(sa, (socklen_t)SLEN(sa),
+ name = Abuf,
+ (unsigned int)sizeof(nbuf), NULL, 0, 0);
+ ADDS(name);
+ }
+ break;
+ case 'P':
+ if (port)
+ ADDS(port);
+ else if (p == -1)
+ ADDNA();
+ else {
+ getnameinfo(sa, (socklen_t)SLEN(sa), NULL, 0,
+ port = pbuf,
+ (unsigned int)sizeof(pbuf), 0);
+ ADDS(port);
+ }
+ break;
+ case 'I':
+#ifdef HAVE_NET_IF_DL_H
+ if (sdl && addr != abuf) {
+ ADDS(abuf);
+ } else
+#endif
+ {
+ ADDNA();
+ }
+ break;
+ case 'F':
+ if (sin6) {
+ (void)snprintf(nbuf, sizeof(nbuf), "%d",
+ sin6->sin6_flowinfo);
+ ADDS(nbuf);
+ break;
+ } else {
+ ADDNA();
+ }
+ break;
+ case 'S':
+ if (sin6) {
+ (void)snprintf(nbuf, sizeof(nbuf), "%d",
+ sin6->sin6_scope_id);
+ ADDS(nbuf);
+ break;
+ } else {
+ ADDNA();
+ }
+ break;
+ case 'R':
+#ifdef HAVE_NETATALK_AT_H
+ if (sat) {
+ const struct netrange *n =
+ &sat->sat_range.r_netrange;
+ (void)snprintf(nbuf, sizeof(nbuf),
+ "%d:[%d,%d]", n->nr_phase , n->nr_firstnet,
+ n->nr_lastnet);
+ ADDS(nbuf);
+ } else
+#endif
+ {
+ ADDNA();
+ }
+ break;
+ case 'D':
+ switch (sa->sa_family) {
+#ifdef HAVE_NETATALK_AT_H
+ case AF_APPLETALK:
+ debug_at(nbuf, sizeof(nbuf), sat);
+ break;
+#endif
+ case AF_LOCAL:
+ debug_un(nbuf, sizeof(nbuf), sun);
+ break;
+ case AF_INET:
+ debug_in(nbuf, sizeof(nbuf), sin4);
+ break;
+ case AF_INET6:
+ debug_in6(nbuf, sizeof(nbuf), sin6);
+ break;
+#ifdef HAVE_NET_IF_DL_H
+ case AF_LINK:
+ debug_dl(nbuf, sizeof(nbuf), sdl);
+ break;
+#endif
+ default:
+ abort();
+ }
+ ADDS(nbuf);
+ break;
+ default:
+ ADDC('%');
+ if (na == 0)
+ ADDC('?');
+ if (*ptr == '\0')
+ goto done;
+ /*FALLTHROUGH*/
+ case '%':
+ ADDC(*ptr);
+ break;
+ }
+ na = 1;
+ }
+done:
+ if (buf < ebuf)
+ *buf = '\0';
+ else if (len != 0)
+ sbuf[len - 1] = '\0';
+ return (int)(buf - sbuf);
+}
diff --git a/port/strlcat.c b/port/strlcat.c
new file mode 100644
index 000000000000..d3c69b5fab78
--- /dev/null
+++ b/port/strlcat.c
@@ -0,0 +1,96 @@
+/* $NetBSD: strlcat.c,v 1.2 2015/01/22 03:48:07 christos Exp $ */
+/* $OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: strlcat.c,v 1.2 2015/01/22 03:48:07 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef _LIBC
+#include "namespace.h"
+#endif
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef _LIBC
+# ifdef __weak_alias
+__weak_alias(strlcat, _strlcat)
+# endif
+#endif
+
+#else
+#include <lib/libkern/libkern.h>
+#endif /* !_KERNEL && !_STANDALONE */
+
+#if !HAVE_STRLCAT
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+#if 1
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+#else
+
+ /*
+ * Find length of string in dst (maxing out at siz).
+ */
+ size_t dlen = strnlen(dst, siz);
+
+ /*
+ * Copy src into any remaining space in dst (truncating if needed).
+ * Note strlcpy(dst, src, 0) returns strlen(src).
+ */
+ return dlen + strlcpy(dst + dlen, src, siz - dlen);
+#endif
+}
+#endif
diff --git a/port/strlcpy.c b/port/strlcpy.c
new file mode 100644
index 000000000000..6646e1ce9696
--- /dev/null
+++ b/port/strlcpy.c
@@ -0,0 +1,78 @@
+/* $NetBSD: strlcpy.c,v 1.2 2015/01/22 03:48:07 christos Exp $ */
+/* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: strlcpy.c,v 1.2 2015/01/22 03:48:07 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef _LIBC
+#include "namespace.h"
+#endif
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef _LIBC
+# ifdef __weak_alias
+__weak_alias(strlcpy, _strlcpy)
+# endif
+#endif
+#else
+#include <lib/libkern/libkern.h>
+#endif /* !_KERNEL && !_STANDALONE */
+
+
+#if !HAVE_STRLCPY
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+#endif
diff --git a/port/strtoi.c b/port/strtoi.c
new file mode 100644
index 000000000000..5514f1a00a32
--- /dev/null
+++ b/port/strtoi.c
@@ -0,0 +1,61 @@
+/* $NetBSD: strtoi.c,v 1.3 2015/01/22 03:10:50 christos Exp $ */
+
+/*-
+ * Copyright (c) 2005 The DragonFly Project. All rights reserved.
+ * Copyright (c) 2003 Citrus Project,
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * Created by Kamil Rytarowski, based on ID:
+ * NetBSD: src/common/lib/libc/stdlib/strtoul.c,v 1.3 2008/08/20 19:58:34 oster Exp
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: strtoi.c,v 1.3 2015/01/22 03:10:50 christos Exp $");
+
+#if defined(_KERNEL)
+#include <sys/param.h>
+#include <sys/types.h>
+#include <lib/libkern/libkern.h>
+#elif defined(_STANDALONE)
+#include <sys/param.h>
+#include <sys/types.h>
+#include <lib/libkern/libkern.h>
+#include <lib/libsa/stand.h>
+#else
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#endif
+
+#define _FUNCNAME strtoi
+#define __TYPE intmax_t
+#define __WRAPPED strtoimax
+
+#if !HAVE_STRTOI
+#include "_strtoi.h"
+#endif
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 000000000000..a451274def06
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,12 @@
+# $NetBSD: Makefile,v 1.3 2015/05/30 22:40:38 christos Exp $
+
+MKMAN=no
+
+PROGS=srvtest cltest
+SRCS.srvtest = srvtest.c
+SRCS.cltest = cltest.c
+CPPFLAGS+=-DBLDEBUG
+LDADD+=-lutil
+DPADD+=${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/test/cltest.c b/test/cltest.c
new file mode 100644
index 000000000000..6671429fc3c4
--- /dev/null
+++ b/test/cltest.c
@@ -0,0 +1,136 @@
+/* $NetBSD: cltest.c,v 1.6 2015/01/22 05:44:28 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: cltest.c,v 1.6 2015/01/22 05:44:28 christos Exp $");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <err.h>
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+static __dead void
+usage(int c)
+{
+ warnx("Unknown option `%c'", (char)c);
+ fprintf(stderr, "Usage: %s [-u] [-a <addr>] [-m <msg>] [-p <port>]\n",
+ getprogname());
+ exit(EXIT_FAILURE);
+}
+
+static void
+getaddr(const char *a, in_port_t p, struct sockaddr_storage *ss,
+ socklen_t *slen)
+{
+ int c;
+
+ memset(ss, 0, sizeof(*ss));
+ p = htons(p);
+
+ if (strchr(a, ':')) {
+ struct sockaddr_in6 *s6 = (void *)ss;
+ c = inet_pton(AF_INET6, a, &s6->sin6_addr);
+ s6->sin6_family = AF_INET6;
+ *slen = sizeof(*s6);
+ s6->sin6_port = p;
+ } else {
+ struct sockaddr_in *s = (void *)ss;
+ c = inet_pton(AF_INET, a, &s->sin_addr);
+ s->sin_family = AF_INET;
+ *slen = sizeof(*s);
+ s->sin_port = p;
+ }
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ ss->ss_len = (uint8_t)*slen;
+#endif
+ if (c == -1)
+ err(EXIT_FAILURE, "Invalid address `%s'", a);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int sfd;
+ int c;
+ struct sockaddr_storage ss;
+ const char *msg = "hello";
+ const char *addr = "127.0.0.1";
+ int type = SOCK_STREAM;
+ in_port_t port = 6161;
+ socklen_t slen;
+ char buf[128];
+
+ while ((c = getopt(argc, argv, "a:m:p:u")) != -1) {
+ switch (c) {
+ case 'a':
+ addr = optarg;
+ break;
+ case 'm':
+ msg = optarg;
+ break;
+ case 'p':
+ port = (in_port_t)atoi(optarg);
+ break;
+ case 'u':
+ type = SOCK_DGRAM;
+ break;
+ default:
+ usage(c);
+ }
+ }
+
+ getaddr(addr, port, &ss, &slen);
+
+ if ((sfd = socket(AF_INET, type, 0)) == -1)
+ err(EXIT_FAILURE, "socket");
+
+ sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (const void *)&ss);
+ printf("connecting to: %s\n", buf);
+ if (connect(sfd, (const void *)&ss, slen) == -1)
+ err(EXIT_FAILURE, "connect");
+
+ size_t len = strlen(msg) + 1;
+ if (write(sfd, msg, len) != (ssize_t)len)
+ err(EXIT_FAILURE, "write");
+ return 0;
+}
diff --git a/test/srvtest.c b/test/srvtest.c
new file mode 100644
index 000000000000..03a762ab81f2
--- /dev/null
+++ b/test/srvtest.c
@@ -0,0 +1,220 @@
+/* $NetBSD: srvtest.c,v 1.10 2015/05/30 22:40:38 christos Exp $ */
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: srvtest.c,v 1.10 2015/05/30 22:40:38 christos Exp $");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <err.h>
+
+#include "blacklist.h"
+#ifdef BLDEBUG
+#include "bl.h"
+static void *b;
+#endif
+
+#ifndef INFTIM
+#define INFTIM -1
+#endif
+
+static void
+process_tcp(int afd)
+{
+ ssize_t n;
+ char buffer[256];
+
+ memset(buffer, 0, sizeof(buffer));
+
+ if ((n = read(afd, buffer, sizeof(buffer))) == -1)
+ err(1, "read");
+ buffer[sizeof(buffer) - 1] = '\0';
+ printf("%s: sending %d %s\n", getprogname(), afd, buffer);
+#ifdef BLDEBUG
+ blacklist_r(b, 1, afd, buffer);
+#else
+ blacklist(1, afd, buffer);
+#endif
+ exit(0);
+}
+
+static void
+process_udp(int afd)
+{
+ ssize_t n;
+ char buffer[256];
+ struct sockaddr_storage ss;
+ socklen_t slen;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ slen = (socklen_t)sizeof(ss);
+ memset(&ss, 0, sizeof(ss));
+ if ((n = recvfrom(afd, buffer, sizeof(buffer), 0, (void *)&ss,
+ &slen)) == -1)
+ err(1, "recvfrom");
+ buffer[sizeof(buffer) - 1] = '\0';
+ printf("%s: sending %d %s\n", getprogname(), afd, buffer);
+ blacklist_sa(1, afd, (void *)&ss, slen, buffer);
+ exit(0);
+}
+static int
+cr(int af, int type, in_port_t p)
+{
+ int sfd;
+ struct sockaddr_storage ss;
+ socklen_t slen;
+ sfd = socket(af == AF_INET ? PF_INET : PF_INET6, type, 0);
+ if (sfd == -1)
+ err(1, "socket");
+
+ p = htons(p);
+ memset(&ss, 0, sizeof(ss));
+ if (af == AF_INET) {
+ struct sockaddr_in *s = (void *)&ss;
+ s->sin_family = AF_INET;
+ slen = sizeof(*s);
+ s->sin_port = p;
+ } else {
+ struct sockaddr_in6 *s6 = (void *)&ss;
+ s6->sin6_family = AF_INET6;
+ slen = sizeof(*s6);
+ s6->sin6_port = p;
+ }
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ ss.ss_len = (uint8_t)slen;
+#endif
+
+ if (bind(sfd, (const void *)&ss, slen) == -1)
+ err(1, "bind");
+
+ if (type != SOCK_DGRAM)
+ if (listen(sfd, 5) == -1)
+ err(1, "listen");
+ return sfd;
+}
+
+static void
+handle(int type, int sfd)
+{
+ struct sockaddr_storage ss;
+ socklen_t alen = sizeof(ss);
+ int afd;
+
+ if (type != SOCK_DGRAM) {
+ if ((afd = accept(sfd, (void *)&ss, &alen)) == -1)
+ err(1, "accept");
+ } else
+ afd = sfd;
+
+ /* Create child process */
+ switch (fork()) {
+ case -1:
+ err(1, "fork");
+ case 0:
+ if (type == SOCK_DGRAM)
+ process_udp(afd);
+ else
+ process_tcp(afd);
+ break;
+ default:
+ close(afd);
+ break;
+ }
+}
+
+static __dead void
+usage(int c)
+{
+ warnx("Unknown option `%c'", (char)c);
+ fprintf(stderr, "Usage: %s [-u] [-p <num>]\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+#ifdef __linux__
+#define NUMFD 1
+#else
+#define NUMFD 2
+#endif
+ struct pollfd pfd[NUMFD];
+ int type = SOCK_STREAM, c;
+ in_port_t port = 6161;
+
+ signal(SIGCHLD, SIG_IGN);
+
+#ifdef BLDEBUG
+ b = bl_create(false, "blsock", vsyslog);
+#endif
+
+ while ((c = getopt(argc, argv, "up:")) != -1)
+ switch (c) {
+ case 'u':
+ type = SOCK_DGRAM;
+ break;
+ case 'p':
+ port = (in_port_t)atoi(optarg);
+ break;
+ default:
+ usage(c);
+ }
+
+ pfd[0].fd = cr(AF_INET, type, port);
+ pfd[0].events = POLLIN;
+#if NUMFD > 1
+ pfd[1].fd = cr(AF_INET6, type, port);
+ pfd[1].events = POLLIN;
+#endif
+
+ for (;;) {
+ if (poll(pfd, __arraycount(pfd), INFTIM) == -1)
+ err(1, "poll");
+ for (size_t i = 0; i < __arraycount(pfd); i++) {
+ if ((pfd[i].revents & POLLIN) == 0)
+ continue;
+ handle(type, pfd[i].fd);
+ }
+ }
+}